Skip to content

Commit

Permalink
feat(alert-dialog): new component (#364)
Browse files Browse the repository at this point in the history
* chore:update alert-dialog

* fix: pnpm lock

* chore: delete test file

* refaktor: move interface and props to props.ts

* fix: props

* chore: update

* fix: code

* chore: add more story

* chore: fix lint

* fix: lock

* feat: add test

* chore: fix type node

---------

Co-authored-by: productdevbook <hi@productdevbook.com>
  • Loading branch information
kbrkiras and productdevbook authored Sep 19, 2023
1 parent d421214 commit a9d0868
Show file tree
Hide file tree
Showing 30 changed files with 2,148 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Enter the component you want most in the components, leave the emojis and follow
| Component | Description | Status | Docs |
| --- | --- | --- | --- |
| [Accordion](https://github.com/oku-ui/primitives/issues/3) | A group of collapsible panels | 🚧 In Progress | - |
| [Alert Dialog](https://github.com/oku-ui/primitives/issues/4) | A modal dialog that interrupts the user's workflow to get a response | 🚧 In Progress | - |
| [Alert Dialog](https://oku-ui.com/primitives/components/alert-dialog) | <span><a href="https://www.npmjs.com/package/@oku-ui/alert-dialog "><img src="https://img.shields.io/npm/v/@oku-ui/alert-dialog.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/alert-dialog"><img src="https://img.shields.io/npm/dm/@oku-ui/alert-dialog.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"></a> </span> | <span> <a href="https://oku-ui.com/primitives/components/alert-dialog"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |
| [Aspect Ratio](https://oku-ui.com/primitives/components/aspect-ratio) | <span><a href="https://www.npmjs.com/package/@oku-ui/aspect-ratio "><img src="https://img.shields.io/npm/v/@oku-ui/aspect-ratio.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/aspect-ratio"><img src="https://img.shields.io/npm/dm/@oku-ui/aspect-ratio.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"></a> </span> | <span> <a href="https://oku-ui.com/primitives/components/aspect-ratio"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |
| [Avatar](https://oku-ui.com/primitives/components/avatar) | <span><a href="https://www.npmjs.com/package/@oku-ui/avatar "><img src="https://img.shields.io/npm/v/@oku-ui/avatar?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/avatar"><img src="https://img.shields.io/npm/dm/@oku-ui/avatar?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"></a></span> | <span> <a href="https://oku-ui.com/primitives/components/avatar"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |
| [Checkbox](https://oku-ui.com/primitives/components/checkbox) | <span><a href="https://www.npmjs.com/package/@oku-ui/checkbox "><img src="https://img.shields.io/npm/v/@oku-ui/checkbox?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/checkbox"> <img src="https://img.shields.io/npm/dm/@oku-ui/checkbox?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/checkbox"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"devDependencies": {
"@egoist/tailwindcss-icons": "^1.1.0",
"@iconify-json/ph": "^1.1.6",
"@oku-ui/alert-dialog": "workspace:^",
"@oku-ui/arrow": "workspace:^",
"@oku-ui/aspect-ratio": "workspace:^",
"@oku-ui/avatar": "workspace:^",
Expand Down Expand Up @@ -120,6 +121,7 @@
},
"pnpm": {
"overrides": {
"@oku-ui/alert-dialog": "workspace:^",
"@oku-ui/arrow": "workspace:^",
"@oku-ui/aspect-ratio": "workspace:^",
"@oku-ui/avatar": "workspace:^",
Expand Down
14 changes: 14 additions & 0 deletions packages/components/alert-dialog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# AlertDialog
Renders an accessible label associated with controls.

![@oku-ui/alert-dialog](./../../../.github/assets/og/oku-alert-dialog.jpg)

<span><a href="https://www.npmjs.com/package/@oku-ui/alert-dialog "><img src="https://img.shields.io/npm/v/@oku-ui/alert-dialog?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/alert-dialog"> <img src="https://img.shields.io/npm/dm/@oku-ui/alert-dialog?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/alert-dialog"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span>

## Installation

```sh
$ pnpm add @oku-ui/alert-dialog
```

[Documentation](https://oku-ui.com/primitives/components/alert-dialog)
7 changes: 7 additions & 0 deletions packages/components/alert-dialog/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild'

const isClean = (process.env.CLEAN || 'false') === 'true'
export default defineBuildConfig({
declaration: true,
clean: isClean,
})
54 changes: 54 additions & 0 deletions packages/components/alert-dialog/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@oku-ui/alert-dialog",
"type": "module",
"version": "0.4.0-alpha.12",
"license": "MIT",
"source": "src/index.ts",
"funding": "https://github.com/sponsors/productdevbook",
"homepage": "https://oku-ui.com/primitives",
"repository": {
"type": "git",
"url": "git+https://github.com/oku-ui/primitives.git",
"directory": "packages/components/label"
},
"bugs": {
"url": "https://github.com/oku-ui/primitives/issues"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
}
},
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"engines": {
"node": ">=18"
},
"scripts": {
"build": "unbuild",
"dev": "unbuild --stub",
"clean": "rimraf ./dist && rimraf ./node_modules"
},
"peerDependencies": {
"vue": "^3.3.0"
},
"dependencies": {
"@oku-ui/dialog": "latest",
"@oku-ui/primitive": "latest",
"@oku-ui/provide": "latest",
"@oku-ui/slot": "latest",
"@oku-ui/use-composable": "latest",
"@oku-ui/utils": "latest",
"@types/node": "^18.17.12"
},
"devDependencies": {
"tsconfig": "workspace:^"
},
"publishConfig": {
"access": "public"
}
}
36 changes: 36 additions & 0 deletions packages/components/alert-dialog/src/alert-dialog-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineComponent, h, mergeProps, reactive, toRefs } from 'vue'
import { primitiveProps } from '@oku-ui/primitive'
import { reactiveOmit, useForwardRef } from '@oku-ui/use-composable'
import { OkuDialogClose } from '@oku-ui/dialog'
import type { AlertDialogActionNaviteElement } from './props'
import { ACTION_NAME, alertDialogActionProps, scopeAlertDialogProps, useAlertDialogScope } from './props'

const alertDialogAction = defineComponent({
name: ACTION_NAME,
inheritAttrs: false,
props: {
...primitiveProps,
...alertDialogActionProps.props,
...scopeAlertDialogProps,
},
setup(props, { attrs, slots }) {
const { scopeOkuAlertDialog, ...actionProps } = toRefs(props)
const _reactive = reactive(actionProps)
const reactiveActionProps = reactiveOmit(_reactive, (key, _value) => key === undefined)

const dialogScope = useAlertDialogScope(scopeOkuAlertDialog.value)
const forwardRef = useForwardRef()

return () => h(OkuDialogClose, {
...dialogScope,
...mergeProps(attrs, reactiveActionProps),
ref: forwardRef,
}, slots)
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuAlertDialogAction = alertDialogAction as typeof alertDialogAction &
(new () => {
$props: AlertDialogActionNaviteElement
})
40 changes: 40 additions & 0 deletions packages/components/alert-dialog/src/alert-dialog-cancel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { defineComponent, h, mergeProps, reactive, toRefs } from 'vue'
import { primitiveProps } from '@oku-ui/primitive'
import { reactiveOmit, useComposedRefs, useForwardRef } from '@oku-ui/use-composable'
import { OkuDialogClose } from '@oku-ui/dialog'
import type { AlertDialogCancelNaviteElement } from './props'
import { CANCEL_NAME, alertDialogCancelProps, scopeAlertDialogProps, useAlertDialogContentInject, useAlertDialogScope } from './props'

const alertDialogCancel = defineComponent({
name: CANCEL_NAME,
inheritAttrs: false,
props: {
...primitiveProps,
...alertDialogCancelProps.props,
...scopeAlertDialogProps,
},
setup(props, { attrs, slots }) {
const { scopeOkuAlertDialog, ...cancelProps } = toRefs(props)
const _reactive = reactive(cancelProps)
const reactiveCancelProps = reactiveOmit(_reactive, (key, _value) => key === undefined)

const dialogScope = useAlertDialogScope(scopeOkuAlertDialog.value)

const { cancelRef } = useAlertDialogContentInject(CANCEL_NAME, scopeOkuAlertDialog.value)

const forwardRef = useForwardRef()
const composedRefs = useComposedRefs(forwardRef, cancelRef)

return () => h(OkuDialogClose, {
...dialogScope,
...mergeProps(attrs, reactiveCancelProps),
ref: composedRefs,
}, slots)
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuAlertDialogCancel = alertDialogCancel as typeof alertDialogCancel &
(new () => {
$props: AlertDialogCancelNaviteElement
})
79 changes: 79 additions & 0 deletions packages/components/alert-dialog/src/alert-dialog-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { computed, defineComponent, h, mergeProps, reactive, ref, toRefs } from 'vue'
import { reactiveOmit, useComposedRefs, useForwardRef } from '@oku-ui/use-composable'
import type { DialogContentModalEmits } from '@oku-ui/dialog'
import { OkuDialogContent, OkuDialogDescriptionWarning, WarningProvider } from '@oku-ui/dialog'
import { composeEventHandlers } from '@oku-ui/utils'
import { OkuSlottable } from '@oku-ui/slot'
import type { AlertDialogContentNaviteElement } from './props'
import { AlertDialogContentProvider, CONTENT_NAME, TITLE_NAME, alertDialogContentProps, scopeAlertDialogProps, useAlertDialogScope } from './props'

const alertDialogContent = defineComponent({
name: CONTENT_NAME,
inheritAttrs: false,
props: {
...alertDialogContentProps.props,
...scopeAlertDialogProps,
},
emits: alertDialogContentProps.emits,
setup(props, { attrs, slots }) {
const { scopeOkuAlertDialog, ...contentProps } = toRefs(props)
const _reactive = reactive(contentProps)
const reactiveContentProps = reactiveOmit(_reactive, (key, _value) => key === undefined)

const dialogScope = useAlertDialogScope(scopeOkuAlertDialog.value)

const forwardRef = useForwardRef()
const contentRef = ref<HTMLDivElement | null>(null)
const composedRefs = useComposedRefs(forwardRef, contentRef)

const cancelRef = ref<HTMLButtonElement | null>(null)

WarningProvider({
contentName: computed(() => CONTENT_NAME),
titleName: computed(() => TITLE_NAME),
docsSlug: computed(() => 'alert-dialog'),
})

AlertDialogContentProvider({
scope: scopeOkuAlertDialog.value,
cancelRef,
})
return () => h(OkuDialogContent, {
role: 'alertdialog',
...dialogScope,
...mergeProps(attrs, reactiveContentProps),
ref: composedRefs,
onOpenAutoFocus: composeEventHandlers<DialogContentModalEmits['openAutoFocus'][0]>((event) => {
event.preventDefault()
}, (el) => {
el.preventDefault()
cancelRef.value?.focus({ preventScroll: true })
}),
onInteractOutside: event => event.preventDefault(),
onPointerdownOutside: event => event.preventDefault(),
},
{
/**
* We have to use `Slottable` here as we cannot wrap the `AlertDialogContentProvider`
* around everything, otherwise the `DescriptionWarning` would be rendered straight away.
* This is because we want the accessibility checks to run only once the content is actually
* open and that behaviour is already encapsulated in `DialogContent`.
*/
default: () => [
h(OkuSlottable, {}, {
default: () => slots.default?.(),
}),
process.env.NODE_ENV === 'development'
&& h(OkuDialogDescriptionWarning, {
contentRef: contentRef.value,
}),
],
})
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuAlertDialogContent = alertDialogContent as typeof alertDialogContent &
(new () => {
$props: AlertDialogContentNaviteElement
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineComponent, toRefs, watchEffect } from 'vue'

import { CONTENT_NAME, DESCRIPTION_NAME, DESCRIPTION_WARNING_NAME, dialogDescriptionWarningProps } from './props'

const alertDialogDescriptionWarning = defineComponent({
name: DESCRIPTION_WARNING_NAME,
inheritAttrs: false,
props: {
...dialogDescriptionWarningProps.props,
},
setup(props, { attrs: _attrs }) {
const { contentRef } = toRefs(props)

const MESSAGE = `\`${CONTENT_NAME}\` requires a description for the component to be accessible for screen reader users.
You can add a description to the \`${CONTENT_NAME}\` by passing a \`${DESCRIPTION_NAME}\` component as a child, which also benefits sighted users by adding visible context to the dialog.
Alternatively, you can use your own component as a description by assigning it an \`id\` and passing the same value to the \`aria-describedby\` prop in \`${CONTENT_NAME}\`. If the description is confusing or duplicative for sighted users, you can use the \`@radix-ui/react-visually-hidden\` primitive as a wrapper around your description component.
For more information, see https://radix-ui.com/primitives/docs/components/alert-dialog`

watchEffect(() => {
if (contentRef.value) {
const hasDescription = document.getElementById(
contentRef.value.getAttribute('aria-describedby')!,
)
if (!hasDescription)
console.warn(MESSAGE)
}
})

return () => null
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuAlertDialogDescriptionWarning = alertDialogDescriptionWarning as typeof alertDialogDescriptionWarning
34 changes: 34 additions & 0 deletions packages/components/alert-dialog/src/alert-dialog-description.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { defineComponent, h, mergeProps, reactive, toRefs } from 'vue'
import { reactiveOmit, useForwardRef } from '@oku-ui/use-composable'
import { OkuDialogDescription } from '@oku-ui/dialog'
import type { AlertDialogDescriptionNaviteElement } from './props'
import { DESCRIPTION_NAME, alertDialogDescriptionProps, scopeAlertDialogProps, useAlertDialogScope } from './props'

const alertDialogDescription = defineComponent({
name: DESCRIPTION_NAME,
inheritAttrs: false,
props: {
...alertDialogDescriptionProps.props,
...scopeAlertDialogProps,
},
setup(props, { attrs, slots }) {
const { scopeOkuAlertDialog, ...descriptionProps } = toRefs(props)
const _reactive = reactive(descriptionProps)
const reactiveDescriptionProps = reactiveOmit(_reactive, (key, _value) => key === undefined)

const dialogScope = useAlertDialogScope(scopeOkuAlertDialog.value)
const forwardRef = useForwardRef()

return () => h(OkuDialogDescription, {
...dialogScope,
...mergeProps(attrs, reactiveDescriptionProps),
ref: forwardRef,
}, slots)
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuAlertDialogDescription = alertDialogDescription as typeof alertDialogDescription &
(new () => {
$props: AlertDialogDescriptionNaviteElement
})
34 changes: 34 additions & 0 deletions packages/components/alert-dialog/src/alert-dialog-overlay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { defineComponent, h, mergeProps, reactive, toRefs } from 'vue'
import { reactiveOmit, useForwardRef } from '@oku-ui/use-composable'
import { OkuDialogOverlay } from '@oku-ui/dialog'
import type { AlertDialogOverlayNaviteElement } from './props'
import { OVERLAY_NAME, alertDialogOverlayProps, scopeAlertDialogProps, useAlertDialogScope } from './props'

const alertDialogOverlay = defineComponent({
name: OVERLAY_NAME,
inheritAttrs: false,
props: {
...alertDialogOverlayProps.props,
...scopeAlertDialogProps,
},
setup(props, { attrs, slots }) {
const { scopeOkuAlertDialog, ...overlayProps } = toRefs(props)
const _reactive = reactive(overlayProps)
const reactiveOverlayProps = reactiveOmit(_reactive, (key, _value) => key === undefined)

const dialogScope = useAlertDialogScope(scopeOkuAlertDialog.value)
const forwardRef = useForwardRef()

return () => h(OkuDialogOverlay, {
...dialogScope,
...mergeProps(attrs, reactiveOverlayProps),
ref: forwardRef,
}, slots)
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuAlertDialogOverlay = alertDialogOverlay as typeof alertDialogOverlay &
(new () => {
$props: AlertDialogOverlayNaviteElement
})
Loading

0 comments on commit a9d0868

Please sign in to comment.