Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: modal implementation #10212

Merged
merged 6 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog/unreleased/change-creating-modals
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Change: Creating modals

BREAKING CHANGE for developers: The way how to work with modals has been reworked. Modals can now be registered via the `dispatchModal` method provided by the `useModals` composable, instead of calling `createModal` or `hideModal` from the store.

For more details on how to use the modal please see the linked PR down below.

https://github.com/owncloud/web/pull/10212
https://github.com/owncloud/web/issues/10095
128 changes: 26 additions & 102 deletions packages/design-system/src/components/OcModal/OcModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,9 @@
v-model="userInputValue"
class="oc-modal-body-input"
:error-message="inputError"
:placeholder="inputPlaceholder"
:label="inputLabel"
:type="inputType"
:description-message="inputDescription"
:disabled="inputDisabled"
:fix-message-line="true"
:selection-range="inputSelectionRange"
@update:model-value="inputOnInput"
Expand All @@ -54,32 +52,32 @@
</div>

<div v-if="!hideActions" class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button
ref="cancelButton"
class="oc-modal-body-actions-cancel"
:variation="buttonCancelVariation"
:appearance="buttonCancelAppearance"
@click="cancelModalAction"
v-text="buttonCancelText"
/>
<oc-button
v-if="!withoutButtonConfirm"
ref="primaryButton"
class="oc-modal-body-actions-confirm oc-ml-s"
variation="primary"
:appearance="buttonConfirmAppearance"
:disabled="buttonConfirmDisabled || !!inputError"
@click="confirm"
v-text="buttonConfirmText"
/>
<div class="oc-modal-body-actions-grid">
<oc-button
class="oc-modal-body-actions-cancel"
variation="passive"
appearance="outline"
@click="cancelModalAction"
>{{ buttonCancelText }}</oc-button
>
<oc-button
v-if="!hideConfirmButton"
class="oc-modal-body-actions-confirm oc-ml-s"
variation="primary"
appearance="filled"
:disabled="buttonConfirmDisabled || !!inputError"
@click="confirm"
>{{ buttonConfirmText }}</oc-button
>
</div>
</div>
</div>
</focus-trap>
</div>
</template>

<script lang="ts">
import { defineComponent, PropType, ComponentPublicInstance, ref, onMounted, unref } from 'vue'
import { defineComponent, PropType, ComponentPublicInstance } from 'vue'
import OcButton from '../OcButton/OcButton.vue'
import OcIcon from '../OcIcon/OcIcon.vue'
import OcTextInput from '../OcTextInput/OcTextInput.vue'
Expand Down Expand Up @@ -174,28 +172,6 @@ export default defineComponent({
required: false,
default: 'Cancel'
},
/**
* Variation type of the cancel button
*/
buttonCancelVariation: {
type: String,
required: false,
default: 'passive',
validator: (value: string) => {
return ['passive', 'primary', 'danger', 'success', 'warning'].includes(value)
}
},
/**
* Appearance of the cancel button
*/
buttonCancelAppearance: {
type: String,
required: false,
default: 'outline',
validator: (value: string) => {
return ['outline', 'filled', 'raw'].includes(value)
}
},
/**
* Text of the confirm button
*/
Expand All @@ -204,17 +180,6 @@ export default defineComponent({
required: false,
default: 'Confirm'
},
/**
* Appearance of the confirm button
*/
buttonConfirmAppearance: {
type: String,
required: false,
default: 'filled',
validator: (value: string) => {
return ['outline', 'filled', 'raw'].includes(value)
}
},
/**
* Asserts whether the confirm action is disabled
*/
Expand All @@ -226,7 +191,7 @@ export default defineComponent({
/**
* Asserts whether the modal should render a confirm button
*/
withoutButtonConfirm: {
hideConfirmButton: {
type: Boolean,
required: false,
default: false
Expand Down Expand Up @@ -270,14 +235,6 @@ export default defineComponent({
required: false,
default: null
},
/**
* Placeholder of the text input field
*/
inputPlaceholder: {
type: String,
required: false,
default: null
},
/**
* Additional description message for the input field
*/
Expand All @@ -294,14 +251,6 @@ export default defineComponent({
required: false,
default: null
},
/**
* Asserts whether the input is disabled
*/
inputDisabled: {
type: Boolean,
required: false,
default: false
},
/**
* Overwrite default focused element
* Can be `#id, .class`.
Expand All @@ -320,34 +269,6 @@ export default defineComponent({
}
},
emits: ['cancel', 'confirm', 'input'],
setup() {
const primaryButton = ref(null)
const cancelButton = ref(null)

const setButtonsEqualWidth = () => {
const _primaryButton = unref(primaryButton)
const _cancelButton = unref(cancelButton)

const primaryWidth = _primaryButton?.$el?.offsetWidth || 0
const cancelWidth = _cancelButton?.$el?.offsetWidth || 0
const maxWidth = Math.max(primaryWidth, cancelWidth)

if (_primaryButton?.$el) {
_primaryButton.$el.style.minWidth = `${maxWidth}px`
}
if (_cancelButton?.$el) {
_cancelButton.$el.style.minWidth = `${maxWidth}px`
}
}
onMounted(() => {
setButtonsEqualWidth()
})

return {
primaryButton,
cancelButton
}
},
data() {
return {
userInputValue: null
Expand Down Expand Up @@ -537,6 +458,12 @@ export default defineComponent({
.oc-button {
border-radius: 4px;
}

&-grid {
display: inline-grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

}
}

Expand Down Expand Up @@ -570,8 +497,6 @@ export default defineComponent({
message="Are you sure you want to delete this file? All its content will be permanently removed. This action cannot be undone."
button-cancel-text="Cancel"
button-confirm-text="Delete"
button-confirm-appearance="filled"
button-confirm-variation="danger"
class="oc-mb-l oc-position-relative"
/>
</div>
Expand All @@ -597,7 +522,6 @@ export default defineComponent({
<oc-modal
title="Rename file lorem.txt"
button-cancel-text="Cancel"
button-cancel-variation="warning"
button-confirm-text="Rename"
class="oc-position-relative"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ exports[`OcModal displays input 1`] = `
<oc-text-input-stub class="oc-modal-body-input" clearbuttonaccessiblelabel="" clearbuttonenabled="false" disabled="false" fixmessageline="true" id="oc-textinput-1" label="Folder name" modelvalue="New folder" passwordpolicy="[object Object]" readonly="false" type="text"></oc-text-input-stub>
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
<div class="oc-modal-body-actions-grid">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
</div>
</focus-trap-stub>
Expand All @@ -36,8 +38,10 @@ exports[`OcModal hides icon if not specified 1`] = `
<!--v-if-->
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
<div class="oc-modal-body-actions-grid">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
</div>
</focus-trap-stub>
Expand All @@ -58,8 +62,10 @@ exports[`OcModal matches snapshot 1`] = `
<!--v-if-->
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
<div class="oc-modal-body-actions-grid">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
</div>
</focus-trap-stub>
Expand All @@ -80,8 +86,10 @@ exports[`OcModal overrides props message with slot 1`] = `
</div>
</div>
<div class="oc-modal-body-actions oc-flex oc-flex-right">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" style="min-width: 0px;" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
<div class="oc-modal-body-actions-grid">
<oc-button-stub appearance="outline" class="oc-modal-body-actions-cancel" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="passive">Cancel</oc-button-stub>
<oc-button-stub appearance="filled" class="oc-modal-body-actions-confirm oc-ml-s" disabled="false" gapsize="medium" justifycontent="center" showspinner="false" size="medium" submit="button" type="button" variation="primary">Confirm</oc-button-stub>
</div>
</div>
</div>
</focus-trap-stub>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<form autocomplete="off" @submit.prevent="onConfirm">
<form autocomplete="off" @submit.prevent="$emit('confirm')">
<oc-text-input
id="create-group-input-display-name"
v-model="group.displayName"
Expand All @@ -10,35 +10,22 @@
@update:model-value="validateDisplayName"
/>
<input type="submit" class="oc-hidden" />
<div class="oc-flex oc-flex-right oc-flex-middle oc-mt-m">
<oc-button
class="oc-modal-body-actions-cancel oc-ml-s"
appearance="outline"
variation="passive"
@click="onCancel"
>{{ $gettext('Cancel') }}
</oc-button>
<oc-button
class="oc-modal-body-actions-confirm oc-ml-s"
appearance="filled"
variation="primary"
:disabled="isFormInvalid"
@click="onConfirm"
>{{ $gettext('Confirm') }}
</oc-button>
</div>
</form>
</template>

<script lang="ts">
import { useGettext } from 'vue3-gettext'
import { computed, defineComponent, ref, unref } from 'vue'
import { computed, defineComponent, ref, PropType, unref, watch } from 'vue'
import { Group } from '@ownclouders/web-client/src/generated'
import { MaybeRef, useClientService, useEventBus, useStore } from '@ownclouders/web-pkg'
import { MaybeRef, Modal, useClientService, useEventBus, useStore } from '@ownclouders/web-pkg'

export default defineComponent({
name: 'CreateGroupModal',
setup(props, { expose }) {
props: {
modal: { type: Object as PropType<Modal>, required: true }
},
emits: ['confirm', 'update:confirmDisabled'],
setup(props, { emit, expose }) {
const { $gettext } = useGettext()
const store = useStore()
const eventBus = useEventBus()
Expand All @@ -58,9 +45,17 @@ export default defineComponent({
.includes(false)
})

watch(
isFormInvalid,
() => {
emit('update:confirmDisabled', unref(isFormInvalid))
},
{ immediate: true }
)

const onConfirm = async () => {
if (unref(isFormInvalid)) {
return
return Promise.reject(new Promise((reject) => reject('')))
JammingBen marked this conversation as resolved.
Show resolved Hide resolved
}

try {
Expand All @@ -76,39 +71,34 @@ export default defineComponent({
title: $gettext('Failed to create group'),
error
})
} finally {
store.dispatch('hideModal')
}
}

const onCancel = () => {
store.dispatch('hideModal')
}

expose({ onConfirm, onCancel })
expose({ onConfirm })

return {
clientService,
group,
formData,
isFormInvalid,
onConfirm,
onCancel

// unit tests
onConfirm
}
},
methods: {
async validateDisplayName() {
this.formData.displayName.valid = false

if (this.group.displayName.trim() === '') {
this.formData.displayName.errorMessage = this.$gettext('Group name cannot be empty')
this.formData.displayName.valid = false
return false
}

if (this.group.displayName.length > 255) {
this.formData.displayName.errorMessage = this.$gettext(
'Group name cannot exceed 255 characters'
)
this.formData.displayName.valid = false
return false
}

Expand All @@ -121,6 +111,7 @@ export default defineComponent({
groupName: this.group.displayName
}
)
this.formData.displayName.valid = false
return false
} catch (e) {}

Expand Down
Loading