@transferwise/components 46.74.0 → 46.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/alert/Alert.js +3 -1
- package/build/alert/Alert.js.map +1 -1
- package/build/alert/Alert.mjs +3 -1
- package/build/alert/Alert.mjs.map +1 -1
- package/build/dimmer/Dimmer.js +3 -1
- package/build/dimmer/Dimmer.js.map +1 -1
- package/build/dimmer/Dimmer.mjs +3 -1
- package/build/dimmer/Dimmer.mjs.map +1 -1
- package/build/drawer/Drawer.js +2 -0
- package/build/drawer/Drawer.js.map +1 -1
- package/build/drawer/Drawer.mjs +2 -0
- package/build/drawer/Drawer.mjs.map +1 -1
- package/build/field/Field.js +2 -0
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +2 -0
- package/build/field/Field.mjs.map +1 -1
- package/build/i18n/en.json +5 -0
- package/build/i18n/en.json.js +5 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +5 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/inlineAlert/InlineAlert.js +3 -1
- package/build/inlineAlert/InlineAlert.js.map +1 -1
- package/build/inlineAlert/InlineAlert.mjs +3 -1
- package/build/inlineAlert/InlineAlert.mjs.map +1 -1
- package/build/modal/Modal.js +3 -0
- package/build/modal/Modal.js.map +1 -1
- package/build/modal/Modal.mjs +3 -0
- package/build/modal/Modal.mjs.map +1 -1
- package/build/statusIcon/StatusIcon.js +50 -16
- package/build/statusIcon/StatusIcon.js.map +1 -1
- package/build/statusIcon/StatusIcon.messages.js +24 -0
- package/build/statusIcon/StatusIcon.messages.js.map +1 -0
- package/build/statusIcon/StatusIcon.messages.mjs +22 -0
- package/build/statusIcon/StatusIcon.messages.mjs.map +1 -0
- package/build/statusIcon/StatusIcon.mjs +48 -14
- package/build/statusIcon/StatusIcon.mjs.map +1 -1
- package/build/types/alert/Alert.d.ts +6 -1
- package/build/types/alert/Alert.d.ts.map +1 -1
- package/build/types/dimmer/Dimmer.d.ts +2 -1
- package/build/types/dimmer/Dimmer.d.ts.map +1 -1
- package/build/types/drawer/Drawer.d.ts +2 -1
- package/build/types/drawer/Drawer.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +6 -1
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/inlineAlert/InlineAlert.d.ts +2 -1
- package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/build/types/modal/Modal.d.ts +2 -1
- package/build/types/modal/Modal.d.ts.map +1 -1
- package/build/types/statusIcon/StatusIcon.d.ts +7 -1
- package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
- package/build/types/statusIcon/StatusIcon.messages.d.ts +29 -0
- package/build/types/statusIcon/StatusIcon.messages.d.ts.map +1 -0
- package/build/types/upload/Upload.d.ts +5 -0
- package/build/types/upload/Upload.d.ts.map +1 -1
- package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts +1 -0
- package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts.map +1 -1
- package/build/types/uploadInput/UploadInput.d.ts +9 -0
- package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
- package/build/types/uploadInput/uploadItem/UploadItem.d.ts +16 -1
- package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
- package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts.map +1 -1
- package/build/upload/Upload.js +4 -2
- package/build/upload/Upload.js.map +1 -1
- package/build/upload/Upload.mjs +4 -2
- package/build/upload/Upload.mjs.map +1 -1
- package/build/upload/steps/uploadImageStep/uploadImageStep.js +6 -3
- package/build/upload/steps/uploadImageStep/uploadImageStep.js.map +1 -1
- package/build/upload/steps/uploadImageStep/uploadImageStep.mjs +6 -3
- package/build/upload/steps/uploadImageStep/uploadImageStep.mjs.map +1 -1
- package/build/uploadInput/UploadInput.js +71 -66
- package/build/uploadInput/UploadInput.js.map +1 -1
- package/build/uploadInput/UploadInput.mjs +72 -67
- package/build/uploadInput/UploadInput.mjs.map +1 -1
- package/build/uploadInput/uploadItem/UploadItem.js +13 -4
- package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
- package/build/uploadInput/uploadItem/UploadItem.mjs +13 -4
- package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
- package/build/uploadInput/uploadItem/UploadItemLink.js +1 -0
- package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -1
- package/build/uploadInput/uploadItem/UploadItemLink.mjs +1 -0
- package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -1
- package/package.json +1 -1
- package/src/alert/Alert.spec.tsx +10 -0
- package/src/alert/Alert.tsx +7 -1
- package/src/dimmer/Dimmer.spec.js +8 -0
- package/src/dimmer/Dimmer.tsx +4 -0
- package/src/drawer/Drawer.spec.js +25 -6
- package/src/drawer/Drawer.tsx +3 -1
- package/src/field/Field.spec.tsx +19 -0
- package/src/field/Field.story.tsx +20 -4
- package/src/field/Field.tsx +7 -1
- package/src/i18n/en.json +5 -0
- package/src/inlineAlert/InlineAlert.spec.tsx +12 -1
- package/src/inlineAlert/InlineAlert.tsx +5 -1
- package/src/modal/Modal.spec.js +19 -1
- package/src/modal/Modal.tsx +4 -0
- package/src/statusIcon/StatusIcon.docs.mdx +28 -0
- package/src/statusIcon/StatusIcon.messages.ts +34 -0
- package/src/statusIcon/StatusIcon.spec.tsx +39 -4
- package/src/statusIcon/StatusIcon.story.tsx +15 -6
- package/src/statusIcon/StatusIcon.tsx +63 -14
- package/src/upload/Upload.spec.js +19 -0
- package/src/upload/Upload.tsx +7 -0
- package/src/upload/steps/uploadImageStep/uploadImageStep.spec.js +13 -0
- package/src/upload/steps/uploadImageStep/uploadImageStep.tsx +15 -4
- package/src/uploadInput/UploadInput.spec.tsx +121 -9
- package/src/uploadInput/UploadInput.tests.story.tsx +207 -140
- package/src/uploadInput/UploadInput.tsx +110 -77
- package/src/uploadInput/uploadItem/UploadItem.spec.tsx +1 -0
- package/src/uploadInput/uploadItem/UploadItem.tsx +30 -6
- package/src/uploadInput/uploadItem/UploadItemLink.tsx +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UploadItem.mjs","sources":["../../../src/uploadInput/uploadItem/UploadItem.tsx"],"sourcesContent":["import { Bin, CheckCircleFill, CrossCircleFill } from '@transferwise/icons';\nimport { clsx } from 'clsx';\nimport { forwardRef, useImperativeHandle, useRef } from 'react';\nimport { useIntl } from 'react-intl';\n\nimport Body from '../../body';\nimport { Size, Status, Typography, Sentiment } from '../../common';\nimport ProcessIndicator from '../../processIndicator/ProcessIndicator';\nimport StatusIcon from '../../statusIcon/StatusIcon';\nimport { UploadedFile, UploadError } from '../types';\n\nimport MESSAGES from './UploadItem.messages';\nimport { UploadItemLink } from './UploadItemLink';\n\nexport type UploadItemProps = React.JSX.IntrinsicAttributes & {\n file: UploadedFile;\n /**\n * Is this Item part of a multiple- or single-file UploadInput\n */\n singleFileUpload: boolean;\n canDelete: boolean;\n onDelete: () => void;\n\n /**\n * Callback to be called when the file link is clicked.\n * When provided, you need to manually trigger actions to load/download the file.\n *\n * @param file\n */\n onDownload?: (file: UploadedFile) => void;\n ref?: React.Ref<UploadItemRef>;\n};\n\ninterface UploadItemRef {\n focus: () => void;\n}\n\nexport enum TEST_IDS {\n uploadItem = 'uploadItem',\n mediaBody = 'mediaBody',\n}\n\nconst UploadItem = forwardRef<UploadItemRef, UploadItemProps>(\n ({ file, canDelete, onDelete, onDownload, singleFileUpload }, ref) => {\n const { formatMessage } = useIntl();\n const { status, filename, error, errors, url } = file;\n const linkRef = useRef<HTMLAnchorElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n useImperativeHandle<UploadItemRef, UploadItemRef>(ref, () => ({\n focus: (): void => {\n if (url) {\n linkRef.current?.focus();\n } else {\n buttonRef.current?.focus();\n }\n },\n }));\n\n const isSucceeded = [Status.SUCCEEDED, undefined].includes(status) && !!url;\n\n /**\n * We're temporarily reverting to the regular icon components,\n * until the StatusIcon receives 24px sizing. Some misalignment\n * to be expected.\n */\n const getIcon = () => {\n if (error || errors?.length || status === Status.FAILED) {\n return <CrossCircleFill size={24} className=\"emphasis--negative\" />;\n }\n\n let processIndicator: React.ReactNode;\n\n switch (status) {\n case Status.PROCESSING:\n case Status.PENDING:\n processIndicator = (\n <ProcessIndicator size={Size.EXTRA_SMALL} status={Status.PROCESSING} />\n );\n break;\n case Status.SUCCEEDED:\n case Status.DONE:\n default:\n processIndicator = <CheckCircleFill size={24} className=\"emphasis--positive\" />;\n }\n\n return processIndicator;\n };\n\n const getErrorMessage = (error?: UploadError) =>\n typeof error === 'object' ? error.message : error || formatMessage(MESSAGES.uploadingFailed);\n\n const getMultipleErrors = (errors?: UploadError[]) => {\n if (!errors?.length) {\n return null;\n }\n\n if (errors?.length === 1) {\n return getErrorMessage(errors[0]);\n }\n\n return (\n <ul className=\"np-upload-input-errors m-b-0\">\n {errors.map((error, index) => {\n // eslint-disable-next-line react/no-array-index-key\n return <li key={index}>{getErrorMessage(error)}</li>;\n })}\n </ul>\n );\n };\n\n const getDescription = () => {\n if (error || errors?.length || status === Status.FAILED) {\n return (\n <Body type={Typography.BODY_DEFAULT_BOLD} className=\"np-upload-input__text text-negative\">\n {errors?.length ? getMultipleErrors(errors) : getErrorMessage(error)}\n </Body>\n );\n }\n\n switch (status) {\n case Status.PENDING:\n return (\n <Body type={Typography.BODY_DEFAULT} className=\"np-upload-input__text\">\n {formatMessage(MESSAGES.uploading)}\n </Body>\n );\n case Status.PROCESSING:\n return <Body className=\"np-upload-input__text\">{formatMessage(MESSAGES.deleting)}</Body>;\n case Status.SUCCEEDED:\n case Status.DONE:\n default:\n return (\n <Body type={Typography.BODY_DEFAULT_BOLD} className=\"np-upload-input__text\">\n {formatMessage(MESSAGES.uploaded)}\n </Body>\n );\n }\n };\n\n const getTitle = () => {\n return filename || formatMessage(MESSAGES.uploadedFile);\n };\n\n const onDownloadFile = (event: React.MouseEvent): void => {\n if (onDownload) {\n event.preventDefault();\n onDownload(file);\n }\n };\n\n return (\n <div\n className={clsx('np-upload-input__item', { 'is-interactive': isSucceeded && url })}\n data-testid={TEST_IDS.uploadItem}\n >\n <UploadItemLink\n ref={linkRef}\n url={isSucceeded ? url : undefined}\n singleFileUpload={singleFileUpload}\n onDownload={onDownloadFile}\n >\n <span className=\"np-upload-input__icon\">{getIcon()}</span>\n <div className=\"np-upload-input__item-content\" data-testid={TEST_IDS.mediaBody}>\n <Body type={Typography.BODY_LARGE} className=\"np-upload-input__title text-word-break\">\n {getTitle()}\n </Body>\n {getDescription()}\n </div>\n </UploadItemLink>\n {canDelete && (\n <div className=\"np-upload-input__item-action\">\n <button\n ref={buttonRef}\n aria-label={formatMessage(MESSAGES.removeFile, { filename })}\n className=\"np-upload-input__item-button\"\n type=\"button\"\n onClick={() => onDelete()}\n >\n <Bin size={16} />\n </button>\n </div>\n )}\n </div>\n );\n },\n);\n\nUploadItem.displayName = 'UploadItem';\n\nexport default UploadItem;\n"],"names":["TEST_IDS","UploadItem","forwardRef","file","canDelete","onDelete","onDownload","singleFileUpload","ref","formatMessage","useIntl","status","filename","error","errors","url","linkRef","useRef","buttonRef","useImperativeHandle","focus","current","isSucceeded","Status","SUCCEEDED","undefined","includes","getIcon","length","FAILED","_jsx","CrossCircleFill","size","className","processIndicator","PROCESSING","PENDING","ProcessIndicator","Size","EXTRA_SMALL","DONE","CheckCircleFill","getErrorMessage","message","MESSAGES","uploadingFailed","getMultipleErrors","children","map","index","getDescription","Body","type","Typography","BODY_DEFAULT_BOLD","BODY_DEFAULT","uploading","deleting","uploaded","getTitle","uploadedFile","onDownloadFile","event","preventDefault","_jsxs","clsx","uploadItem","UploadItemLink","mediaBody","BODY_LARGE","removeFile","onClick","Bin","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UploadItem.mjs","sources":["../../../src/uploadInput/uploadItem/UploadItem.tsx"],"sourcesContent":["import { Bin, CheckCircleFill, CrossCircleFill } from '@transferwise/icons';\nimport { clsx } from 'clsx';\nimport { forwardRef, useImperativeHandle, useRef } from 'react';\nimport { useIntl } from 'react-intl';\n\nimport Body from '../../body';\nimport { Size, Status, Typography } from '../../common';\nimport ProcessIndicator from '../../processIndicator/ProcessIndicator';\nimport { UploadedFile, UploadError } from '../types';\n\nimport MESSAGES from './UploadItem.messages';\nimport { UploadItemLink } from './UploadItemLink';\n\nexport type UploadItemProps = React.JSX.IntrinsicAttributes & {\n file: UploadedFile;\n /**\n * Is this Item part of a multiple- or single-file UploadInput\n */\n singleFileUpload: boolean;\n canDelete: boolean;\n onDelete: () => void;\n onFocus: () => void;\n\n /**\n * Callback to be called when the file link is clicked.\n * When provided, you need to manually trigger actions to load/download the file.\n *\n * @param file\n */\n onDownload?: (file: UploadedFile) => void;\n ref?: React.Ref<UploadItemRef>;\n};\ninterface UploadItemRef {\n /**\n * A method to set focus on the upload item.\n * @returns {void}\n */\n focus: () => void;\n\n /**\n * A required unique identifier for the upload item.\n */\n id: string | number;\n\n /**\n * An optional status of the upload item.\n */\n status?: string;\n}\n\nexport enum TEST_IDS {\n uploadItem = 'uploadItem',\n mediaBody = 'mediaBody',\n link = 'link',\n action = 'action',\n}\n\nconst UploadItem = forwardRef<UploadItemRef, UploadItemProps>(\n ({ file, canDelete, onDelete, onDownload, singleFileUpload, onFocus: handleFocus }, ref) => {\n const { formatMessage } = useIntl();\n const { status, filename, error, errors, url } = file;\n const linkRef = useRef<HTMLAnchorElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n useImperativeHandle<UploadItemRef, UploadItemRef>(ref, () => ({\n focus: (): void => {\n if (url) {\n linkRef.current?.focus();\n } else {\n buttonRef.current?.focus();\n }\n },\n id: file.id,\n status: file.status,\n }));\n\n const isSucceeded = [Status.SUCCEEDED, undefined].includes(status) && !!url;\n\n /**\n * We're temporarily reverting to the regular icon components,\n * until the StatusIcon receives 24px sizing. Some misalignment\n * to be expected.\n */\n const getIcon = () => {\n if (error || errors?.length || status === Status.FAILED) {\n return <CrossCircleFill size={24} className=\"emphasis--negative\" />;\n }\n\n let processIndicator: React.ReactNode;\n\n switch (status) {\n case Status.PROCESSING:\n case Status.PENDING:\n processIndicator = (\n <ProcessIndicator size={Size.EXTRA_SMALL} status={Status.PROCESSING} />\n );\n break;\n case Status.SUCCEEDED:\n case Status.DONE:\n default:\n processIndicator = <CheckCircleFill size={24} className=\"emphasis--positive\" />;\n }\n\n return processIndicator;\n };\n\n const getErrorMessage = (error?: UploadError) =>\n typeof error === 'object' ? error.message : error || formatMessage(MESSAGES.uploadingFailed);\n\n const getMultipleErrors = (errors?: UploadError[]) => {\n if (!errors?.length) {\n return null;\n }\n\n if (errors?.length === 1) {\n return getErrorMessage(errors[0]);\n }\n\n return (\n <ul className=\"np-upload-input-errors m-b-0\">\n {errors.map((error, index) => {\n // eslint-disable-next-line react/no-array-index-key\n return <li key={index}>{getErrorMessage(error)}</li>;\n })}\n </ul>\n );\n };\n\n const getDescription = () => {\n if (error || errors?.length || status === Status.FAILED) {\n return (\n <Body type={Typography.BODY_DEFAULT_BOLD} className=\"np-upload-input__text text-negative\">\n {errors?.length ? getMultipleErrors(errors) : getErrorMessage(error)}\n </Body>\n );\n }\n\n switch (status) {\n case Status.PENDING:\n return (\n <Body type={Typography.BODY_DEFAULT} className=\"np-upload-input__text\">\n {formatMessage(MESSAGES.uploading)}\n </Body>\n );\n case Status.PROCESSING:\n return <Body className=\"np-upload-input__text\">{formatMessage(MESSAGES.deleting)}</Body>;\n case Status.SUCCEEDED:\n case Status.DONE:\n default:\n return (\n <Body type={Typography.BODY_DEFAULT_BOLD} className=\"np-upload-input__text\">\n {formatMessage(MESSAGES.uploaded)}\n </Body>\n );\n }\n };\n\n const getTitle = () => {\n return filename || formatMessage(MESSAGES.uploadedFile);\n };\n\n const onDownloadFile = (event: React.MouseEvent): void => {\n if (onDownload) {\n event.preventDefault();\n onDownload(file);\n }\n };\n\n return (\n <div\n className={clsx('np-upload-input__item', { 'is-interactive': isSucceeded && url })}\n data-testid={`${file.id}-${TEST_IDS.uploadItem}`}\n >\n <UploadItemLink\n ref={linkRef}\n url={isSucceeded ? url : undefined}\n singleFileUpload={singleFileUpload}\n data-testid={`${file.id}-${TEST_IDS.link}`}\n onDownload={onDownloadFile}\n >\n <span className=\"np-upload-input__icon\">{getIcon()}</span>\n <div\n className=\"np-upload-input__item-content\"\n data-testid={`${file.id}-${TEST_IDS.mediaBody}`}\n >\n <Body type={Typography.BODY_LARGE} className=\"np-upload-input__title text-word-break\">\n {getTitle()}\n </Body>\n {getDescription()}\n </div>\n </UploadItemLink>\n {canDelete && (\n <div className=\"np-upload-input__item-action\">\n <button\n ref={buttonRef}\n aria-label={formatMessage(MESSAGES.removeFile, { filename })}\n className=\"np-upload-input__item-button\"\n type=\"button\"\n tabIndex={0}\n data-testid={`${file.id}-${TEST_IDS.action}`}\n onClick={() => onDelete()}\n onFocus={handleFocus}\n >\n <Bin size={16} />\n </button>\n </div>\n )}\n </div>\n );\n },\n);\n\nUploadItem.displayName = 'UploadItem';\n\nexport default UploadItem;\n"],"names":["TEST_IDS","UploadItem","forwardRef","file","canDelete","onDelete","onDownload","singleFileUpload","onFocus","handleFocus","ref","formatMessage","useIntl","status","filename","error","errors","url","linkRef","useRef","buttonRef","useImperativeHandle","focus","current","id","isSucceeded","Status","SUCCEEDED","undefined","includes","getIcon","length","FAILED","_jsx","CrossCircleFill","size","className","processIndicator","PROCESSING","PENDING","ProcessIndicator","Size","EXTRA_SMALL","DONE","CheckCircleFill","getErrorMessage","message","MESSAGES","uploadingFailed","getMultipleErrors","children","map","index","getDescription","Body","type","Typography","BODY_DEFAULT_BOLD","BODY_DEFAULT","uploading","deleting","uploaded","getTitle","uploadedFile","onDownloadFile","event","preventDefault","_jsxs","clsx","uploadItem","UploadItemLink","link","mediaBody","BODY_LARGE","removeFile","tabIndex","action","onClick","Bin","displayName"],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@ const UploadItemLink = /*#__PURE__*/React.forwardRef(({
|
|
|
23
23
|
target: "_blank",
|
|
24
24
|
rel: "noopener noreferrer",
|
|
25
25
|
className: clsx.clsx('np-upload-input__item-link', singleFileUpload ? 'np-upload-input__item-link--single-file' : ''),
|
|
26
|
+
tabIndex: 0,
|
|
26
27
|
onClick: onDownload,
|
|
27
28
|
children: children
|
|
28
29
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UploadItemLink.js","sources":["../../../src/uploadInput/uploadItem/UploadItemLink.tsx"],"sourcesContent":["import { PropsWithChildren, MouseEvent, forwardRef } from 'react';\nimport { clsx } from 'clsx';\n\ntype UploadItemLinkProps = PropsWithChildren<{\n url?: string;\n onDownload?: (event: MouseEvent) => void;\n singleFileUpload: boolean;\n}>;\n\nexport const UploadItemLink = forwardRef<HTMLAnchorElement | HTMLDivElement, UploadItemLinkProps>(\n ({ children, url, onDownload, singleFileUpload }, ref) => {\n if (!url) {\n return <div
|
|
1
|
+
{"version":3,"file":"UploadItemLink.js","sources":["../../../src/uploadInput/uploadItem/UploadItemLink.tsx"],"sourcesContent":["import { PropsWithChildren, MouseEvent, forwardRef } from 'react';\nimport { clsx } from 'clsx';\n\ntype UploadItemLinkProps = PropsWithChildren<{\n url?: string;\n onDownload?: (event: MouseEvent) => void;\n singleFileUpload: boolean;\n}>;\n\nexport const UploadItemLink = forwardRef<HTMLAnchorElement | HTMLDivElement, UploadItemLinkProps>(\n ({ children, url, onDownload, singleFileUpload }, ref) => {\n if (!url) {\n return (\n <div\n ref={ref as React.RefObject<HTMLDivElement>}\n className={clsx('np-upload-input__item-container')}\n >\n {children}\n </div>\n );\n }\n\n return (\n <a\n ref={ref as React.RefObject<HTMLAnchorElement>}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(\n 'np-upload-input__item-link',\n singleFileUpload ? 'np-upload-input__item-link--single-file' : '',\n )}\n tabIndex={0}\n onClick={onDownload}\n >\n {children}\n </a>\n );\n },\n);\n"],"names":["UploadItemLink","forwardRef","children","url","onDownload","singleFileUpload","ref","_jsx","className","clsx","href","target","rel","tabIndex","onClick"],"mappings":";;;;;;AASaA,MAAAA,cAAc,gBAAGC,gBAAU,CACtC,CAAC;EAAEC,QAAQ;EAAEC,GAAG;EAAEC,UAAU;AAAEC,EAAAA,gBAAAA;CAAkB,EAAEC,GAAG,KAAI;EACvD,IAAI,CAACH,GAAG,EAAE;AACR,IAAA,oBACEI,cAAA,CAAA,KAAA,EAAA;AACED,MAAAA,GAAG,EAAEA,GAAuC;AAC5CE,MAAAA,SAAS,EAAEC,SAAI,CAAC,iCAAiC,CAAE;AAAAP,MAAAA,QAAA,EAElDA,QAAAA;AAAQ,KACN,CAAC,CAAA;AAEV,GAAA;AAEA,EAAA,oBACEK,cAAA,CAAA,GAAA,EAAA;AACED,IAAAA,GAAG,EAAEA,GAA0C;AAC/CI,IAAAA,IAAI,EAAEP,GAAI;AACVQ,IAAAA,MAAM,EAAC,QAAQ;AACfC,IAAAA,GAAG,EAAC,qBAAqB;IACzBJ,SAAS,EAAEC,SAAI,CACb,4BAA4B,EAC5BJ,gBAAgB,GAAG,yCAAyC,GAAG,EAAE,CACjE;AACFQ,IAAAA,QAAQ,EAAE,CAAE;AACZC,IAAAA,OAAO,EAAEV,UAAW;AAAAF,IAAAA,QAAA,EAEnBA,QAAAA;AAAQ,GACR,CAAC,CAAA;AAER,CAAC;;;;"}
|
|
@@ -21,6 +21,7 @@ const UploadItemLink = /*#__PURE__*/forwardRef(({
|
|
|
21
21
|
target: "_blank",
|
|
22
22
|
rel: "noopener noreferrer",
|
|
23
23
|
className: clsx('np-upload-input__item-link', singleFileUpload ? 'np-upload-input__item-link--single-file' : ''),
|
|
24
|
+
tabIndex: 0,
|
|
24
25
|
onClick: onDownload,
|
|
25
26
|
children: children
|
|
26
27
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UploadItemLink.mjs","sources":["../../../src/uploadInput/uploadItem/UploadItemLink.tsx"],"sourcesContent":["import { PropsWithChildren, MouseEvent, forwardRef } from 'react';\nimport { clsx } from 'clsx';\n\ntype UploadItemLinkProps = PropsWithChildren<{\n url?: string;\n onDownload?: (event: MouseEvent) => void;\n singleFileUpload: boolean;\n}>;\n\nexport const UploadItemLink = forwardRef<HTMLAnchorElement | HTMLDivElement, UploadItemLinkProps>(\n ({ children, url, onDownload, singleFileUpload }, ref) => {\n if (!url) {\n return <div
|
|
1
|
+
{"version":3,"file":"UploadItemLink.mjs","sources":["../../../src/uploadInput/uploadItem/UploadItemLink.tsx"],"sourcesContent":["import { PropsWithChildren, MouseEvent, forwardRef } from 'react';\nimport { clsx } from 'clsx';\n\ntype UploadItemLinkProps = PropsWithChildren<{\n url?: string;\n onDownload?: (event: MouseEvent) => void;\n singleFileUpload: boolean;\n}>;\n\nexport const UploadItemLink = forwardRef<HTMLAnchorElement | HTMLDivElement, UploadItemLinkProps>(\n ({ children, url, onDownload, singleFileUpload }, ref) => {\n if (!url) {\n return (\n <div\n ref={ref as React.RefObject<HTMLDivElement>}\n className={clsx('np-upload-input__item-container')}\n >\n {children}\n </div>\n );\n }\n\n return (\n <a\n ref={ref as React.RefObject<HTMLAnchorElement>}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(\n 'np-upload-input__item-link',\n singleFileUpload ? 'np-upload-input__item-link--single-file' : '',\n )}\n tabIndex={0}\n onClick={onDownload}\n >\n {children}\n </a>\n );\n },\n);\n"],"names":["UploadItemLink","forwardRef","children","url","onDownload","singleFileUpload","ref","_jsx","className","clsx","href","target","rel","tabIndex","onClick"],"mappings":";;;;AASaA,MAAAA,cAAc,gBAAGC,UAAU,CACtC,CAAC;EAAEC,QAAQ;EAAEC,GAAG;EAAEC,UAAU;AAAEC,EAAAA,gBAAAA;CAAkB,EAAEC,GAAG,KAAI;EACvD,IAAI,CAACH,GAAG,EAAE;AACR,IAAA,oBACEI,GAAA,CAAA,KAAA,EAAA;AACED,MAAAA,GAAG,EAAEA,GAAuC;AAC5CE,MAAAA,SAAS,EAAEC,IAAI,CAAC,iCAAiC,CAAE;AAAAP,MAAAA,QAAA,EAElDA,QAAAA;AAAQ,KACN,CAAC,CAAA;AAEV,GAAA;AAEA,EAAA,oBACEK,GAAA,CAAA,GAAA,EAAA;AACED,IAAAA,GAAG,EAAEA,GAA0C;AAC/CI,IAAAA,IAAI,EAAEP,GAAI;AACVQ,IAAAA,MAAM,EAAC,QAAQ;AACfC,IAAAA,GAAG,EAAC,qBAAqB;IACzBJ,SAAS,EAAEC,IAAI,CACb,4BAA4B,EAC5BJ,gBAAgB,GAAG,yCAAyC,GAAG,EAAE,CACjE;AACFQ,IAAAA,QAAQ,EAAE,CAAE;AACZC,IAAAA,OAAO,EAAEV,UAAW;AAAAF,IAAAA,QAAA,EAEnBA,QAAAA;AAAQ,GACR,CAAC,CAAA;AAER,CAAC;;;;"}
|
package/package.json
CHANGED
package/src/alert/Alert.spec.tsx
CHANGED
|
@@ -241,6 +241,16 @@ describe('Alert', () => {
|
|
|
241
241
|
});
|
|
242
242
|
});
|
|
243
243
|
|
|
244
|
+
describe('StatusIcon label override', () => {
|
|
245
|
+
it('should accept the accessible name override for the icon', () => {
|
|
246
|
+
const customIconLabel = 'Custom icon label';
|
|
247
|
+
render(
|
|
248
|
+
<Alert type={Sentiment.NEGATIVE} message={message} statusIconLabel={customIconLabel} />,
|
|
249
|
+
);
|
|
250
|
+
expect(screen.getByLabelText(customIconLabel)).toBeInTheDocument();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
244
254
|
describe('onDismiss', () => {
|
|
245
255
|
it('renders the close button if onDismiss is provided', () => {
|
|
246
256
|
render(<Alert message={message} onDismiss={jest.fn()} />);
|
package/src/alert/Alert.tsx
CHANGED
|
@@ -50,6 +50,11 @@ export interface AlertProps {
|
|
|
50
50
|
className?: string;
|
|
51
51
|
/** An optional icon. If not provided, we will default the icon to something appropriate for the type */
|
|
52
52
|
icon?: React.ReactNode;
|
|
53
|
+
/**
|
|
54
|
+
* Override for [StatusIcon's default, accessible name](/?path=/docs/other-statusicon-accessibility--docs)
|
|
55
|
+
* announced by the screen readers
|
|
56
|
+
* */
|
|
57
|
+
statusIconLabel?: string;
|
|
53
58
|
/** Title for the alert component */
|
|
54
59
|
title?: string;
|
|
55
60
|
/** The main body of the alert. Accepts plain text and bold words specified with **double stars */
|
|
@@ -91,6 +96,7 @@ export default function Alert({
|
|
|
91
96
|
className,
|
|
92
97
|
dismissible,
|
|
93
98
|
icon,
|
|
99
|
+
statusIconLabel,
|
|
94
100
|
onDismiss,
|
|
95
101
|
message,
|
|
96
102
|
size,
|
|
@@ -189,7 +195,7 @@ export default function Alert({
|
|
|
189
195
|
{icon ? (
|
|
190
196
|
<div className="alert__icon">{icon}</div>
|
|
191
197
|
) : (
|
|
192
|
-
<StatusIcon size={Size.LARGE} sentiment={resolvedType} />
|
|
198
|
+
<StatusIcon size={Size.LARGE} sentiment={resolvedType} iconLabel={statusIconLabel} />
|
|
193
199
|
)}
|
|
194
200
|
<div className="alert__message">
|
|
195
201
|
<div>
|
|
@@ -15,6 +15,7 @@ describe('Dimmer', () => {
|
|
|
15
15
|
children: <div />,
|
|
16
16
|
className: undefined,
|
|
17
17
|
onClose: undefined,
|
|
18
|
+
onExited: jest.fn(),
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
beforeEach(() => {
|
|
@@ -76,4 +77,11 @@ describe('Dimmer', () => {
|
|
|
76
77
|
exit: 'dimmer--exit dimmer--exit-fade',
|
|
77
78
|
});
|
|
78
79
|
});
|
|
80
|
+
|
|
81
|
+
it('calls onExited when the exit animation is finished', () => {
|
|
82
|
+
component.setProps({ fadeContentOnExit: true });
|
|
83
|
+
const transition = component.find('CSSTransition');
|
|
84
|
+
transition.prop('onExited')();
|
|
85
|
+
expect(props.onExited).toHaveBeenCalled();
|
|
86
|
+
});
|
|
79
87
|
});
|
package/src/dimmer/Dimmer.tsx
CHANGED
|
@@ -31,6 +31,7 @@ export type DimmerProps = CommonProps & {
|
|
|
31
31
|
scrollable?: boolean;
|
|
32
32
|
transparent?: boolean;
|
|
33
33
|
onClose?: (event: KeyboardEvent | MouseEvent) => void;
|
|
34
|
+
onExited?: () => void;
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export const handleTouchMove = (event: Event) => {
|
|
@@ -56,6 +57,7 @@ const Dimmer = ({
|
|
|
56
57
|
scrollable = false,
|
|
57
58
|
transparent = false,
|
|
58
59
|
onClose,
|
|
60
|
+
onExited: handleExited,
|
|
59
61
|
}: DimmerProps) => {
|
|
60
62
|
const [hasNotExited, setHasNotExited] = useState(false);
|
|
61
63
|
const dimmerReference = useRef<HTMLDivElement>(null);
|
|
@@ -101,6 +103,8 @@ const Dimmer = ({
|
|
|
101
103
|
if (dimmerReference.current) {
|
|
102
104
|
dimmerManager.remove(dimmerReference.current);
|
|
103
105
|
}
|
|
106
|
+
|
|
107
|
+
handleExited?.();
|
|
104
108
|
};
|
|
105
109
|
|
|
106
110
|
useEffect(() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mount } from 'enzyme';
|
|
2
2
|
|
|
3
3
|
import { mockMatchMedia } from '../test-utils';
|
|
4
4
|
import Title from '../title/Title';
|
|
@@ -12,7 +12,7 @@ jest.useFakeTimers();
|
|
|
12
12
|
jest.mock(
|
|
13
13
|
'../dimmer',
|
|
14
14
|
() =>
|
|
15
|
-
function ({ open, children }) {
|
|
15
|
+
function Dimmer({ open, children }) {
|
|
16
16
|
return open ? <div className="dimmer">{children}</div> : null;
|
|
17
17
|
},
|
|
18
18
|
);
|
|
@@ -20,7 +20,7 @@ jest.mock(
|
|
|
20
20
|
jest.mock(
|
|
21
21
|
'../slidingPanel',
|
|
22
22
|
() =>
|
|
23
|
-
function ({ open, children }) {
|
|
23
|
+
function SlidingPanel({ open, children }) {
|
|
24
24
|
return open ? <div className="sliding-panel">{children}</div> : null;
|
|
25
25
|
},
|
|
26
26
|
);
|
|
@@ -29,10 +29,10 @@ const defaultLocale = 'en-GB';
|
|
|
29
29
|
|
|
30
30
|
jest.mock('react-intl', () => ({
|
|
31
31
|
injectIntl: (Component) =>
|
|
32
|
-
function (props) {
|
|
32
|
+
function InjectedComponent(props) {
|
|
33
33
|
return <Component {...props} intl={{ locale: defaultLocale }} />;
|
|
34
34
|
},
|
|
35
|
-
useIntl: () => ({ locale: defaultLocale, formatMessage: (id) =>
|
|
35
|
+
useIntl: () => ({ locale: defaultLocale, formatMessage: (id) => String(id) }),
|
|
36
36
|
defineMessages: (translations) => translations,
|
|
37
37
|
}));
|
|
38
38
|
|
|
@@ -48,11 +48,12 @@ describe('Drawer', () => {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
beforeEach(() => {
|
|
51
|
-
component =
|
|
51
|
+
component = mount(<Drawer {...props} />);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
afterEach(() => {
|
|
55
55
|
jest.clearAllMocks();
|
|
56
|
+
component.unmount();
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
it('renders drawer header if title is provided', () => {
|
|
@@ -79,4 +80,22 @@ describe('Drawer', () => {
|
|
|
79
80
|
component.setProps({ footerContent: 'SomeContent' });
|
|
80
81
|
expect(component.find('.np-drawer-footer')).toHaveLength(1);
|
|
81
82
|
});
|
|
83
|
+
|
|
84
|
+
it('passes onUnmount to Dimmer onExited prop', () => {
|
|
85
|
+
const onUnmount = jest.fn();
|
|
86
|
+
component.setProps({ onUnmount });
|
|
87
|
+
component.setProps({ open: true });
|
|
88
|
+
expect(component.find('Dimmer').prop('onExited')).toBe(onUnmount);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('calls onUnmount when the component unmounts', () => {
|
|
92
|
+
const onUnmount = jest.fn();
|
|
93
|
+
component.setProps({ onUnmount });
|
|
94
|
+
component.setProps({ open: true });
|
|
95
|
+
expect(onUnmount).not.toHaveBeenCalled();
|
|
96
|
+
jest.runAllTimers();
|
|
97
|
+
component.setProps({ open: false });
|
|
98
|
+
component.find('Dimmer').prop('onExited')();
|
|
99
|
+
expect(onUnmount).toHaveBeenCalledTimes(1);
|
|
100
|
+
});
|
|
82
101
|
});
|
package/src/drawer/Drawer.tsx
CHANGED
|
@@ -24,6 +24,7 @@ export type DrawerProps = {
|
|
|
24
24
|
position?: `${Position.LEFT | Position.RIGHT | Position.BOTTOM}`;
|
|
25
25
|
/** The action to perform on close click. */
|
|
26
26
|
onClose?: (event: KeyboardEvent | React.MouseEvent) => void;
|
|
27
|
+
onUnmount?: () => void;
|
|
27
28
|
} & Pick<HTMLAttributes<HTMLDivElement>, 'role' | 'aria-labelledby'>;
|
|
28
29
|
|
|
29
30
|
export default function Drawer({
|
|
@@ -32,6 +33,7 @@ export default function Drawer({
|
|
|
32
33
|
footerContent,
|
|
33
34
|
headerTitle,
|
|
34
35
|
onClose,
|
|
36
|
+
onUnmount,
|
|
35
37
|
open = false,
|
|
36
38
|
position = 'right',
|
|
37
39
|
role = 'dialog',
|
|
@@ -48,7 +50,7 @@ export default function Drawer({
|
|
|
48
50
|
const overlayId = useContext(OverlayIdContext);
|
|
49
51
|
|
|
50
52
|
return (
|
|
51
|
-
<Dimmer open={open} onClose={onClose}>
|
|
53
|
+
<Dimmer open={open} onClose={onClose} onExited={onUnmount}>
|
|
52
54
|
<SlidingPanel open={open} position={isMobile ? Position.BOTTOM : position}>
|
|
53
55
|
<div
|
|
54
56
|
id={overlayId}
|
package/src/field/Field.spec.tsx
CHANGED
|
@@ -71,6 +71,25 @@ describe('Field', () => {
|
|
|
71
71
|
expect(screen.getByLabelText(/Phone number/)).toHaveAttribute('aria-describedby');
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
it("should allow for InlineAlert's StatusIcon override via `messageIconLabel` prop", () => {
|
|
75
|
+
const customIconLabel = 'My custom icon label';
|
|
76
|
+
|
|
77
|
+
render(
|
|
78
|
+
<Field
|
|
79
|
+
label="Phone number"
|
|
80
|
+
description="This is help text"
|
|
81
|
+
sentiment="negative"
|
|
82
|
+
message="This is error text"
|
|
83
|
+
messageIconLabel={customIconLabel}
|
|
84
|
+
>
|
|
85
|
+
<Input />
|
|
86
|
+
</Field>,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect(screen.queryByRole('graphics-symbol', { name: 'Error.' })).not.toBeInTheDocument();
|
|
90
|
+
expect(screen.getByRole('graphics-symbol', { name: customIconLabel })).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
74
93
|
it('should show or hide (Optional) suffix depending on required prop', () => {
|
|
75
94
|
render(
|
|
76
95
|
<Field label="Phone number" required={false} description="This is help text">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Input } from '../inputs/Input';
|
|
4
|
-
import { Field } from './Field';
|
|
4
|
+
import { Field, FieldProps } from './Field';
|
|
5
5
|
import { Sentiment } from '../common';
|
|
6
6
|
import DateInput from '../dateInput';
|
|
7
7
|
import { fn } from '@storybook/test';
|
|
@@ -14,9 +14,14 @@ export default {
|
|
|
14
14
|
component: Field,
|
|
15
15
|
title: 'Field',
|
|
16
16
|
tags: ['autodocs'],
|
|
17
|
+
argTypes: {
|
|
18
|
+
messageIconLabel: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
17
22
|
};
|
|
18
23
|
|
|
19
|
-
export const Basic = () => {
|
|
24
|
+
export const Basic = (args: FieldProps) => {
|
|
20
25
|
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
21
26
|
return (
|
|
22
27
|
<div className="row">
|
|
@@ -40,6 +45,7 @@ export const Basic = () => {
|
|
|
40
45
|
description="This a field Description"
|
|
41
46
|
sentiment={Sentiment.NEGATIVE}
|
|
42
47
|
message="Validation error, please take a look"
|
|
48
|
+
messageIconLabel={args.messageIconLabel}
|
|
43
49
|
>
|
|
44
50
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
45
51
|
</Field>
|
|
@@ -74,6 +80,7 @@ export const Basic = () => {
|
|
|
74
80
|
description="This a TextArea Description"
|
|
75
81
|
message={lorem10}
|
|
76
82
|
sentiment="negative"
|
|
83
|
+
messageIconLabel={args.messageIconLabel}
|
|
77
84
|
>
|
|
78
85
|
<TextArea />
|
|
79
86
|
</Field>
|
|
@@ -93,7 +100,7 @@ export const Basic = () => {
|
|
|
93
100
|
);
|
|
94
101
|
};
|
|
95
102
|
|
|
96
|
-
export const WithStatusMessages = () => {
|
|
103
|
+
export const WithStatusMessages = (args: FieldProps) => {
|
|
97
104
|
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
98
105
|
return (
|
|
99
106
|
<div>
|
|
@@ -101,6 +108,7 @@ export const WithStatusMessages = () => {
|
|
|
101
108
|
label="Text Input with Positive Message"
|
|
102
109
|
sentiment={Sentiment.POSITIVE}
|
|
103
110
|
message="Positive message"
|
|
111
|
+
messageIconLabel={args.messageIconLabel}
|
|
104
112
|
>
|
|
105
113
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
106
114
|
</Field>
|
|
@@ -109,6 +117,7 @@ export const WithStatusMessages = () => {
|
|
|
109
117
|
label="Text Input with Warning Message"
|
|
110
118
|
sentiment={Sentiment.WARNING}
|
|
111
119
|
message="Warning message"
|
|
120
|
+
messageIconLabel={args.messageIconLabel}
|
|
112
121
|
>
|
|
113
122
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
114
123
|
</Field>
|
|
@@ -117,6 +126,7 @@ export const WithStatusMessages = () => {
|
|
|
117
126
|
label="Text Input with Validation Error"
|
|
118
127
|
sentiment={Sentiment.NEGATIVE}
|
|
119
128
|
message="This is a required field"
|
|
129
|
+
messageIconLabel={args.messageIconLabel}
|
|
120
130
|
>
|
|
121
131
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
122
132
|
</Field>
|
|
@@ -127,6 +137,7 @@ export const WithStatusMessages = () => {
|
|
|
127
137
|
hint="This is a helpful message"
|
|
128
138
|
sentiment={Sentiment.NEGATIVE}
|
|
129
139
|
message="Validation error, please take a look"
|
|
140
|
+
messageIconLabel={args.messageIconLabel}
|
|
130
141
|
>
|
|
131
142
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
132
143
|
</Field>
|
|
@@ -136,12 +147,17 @@ export const WithStatusMessages = () => {
|
|
|
136
147
|
label="Text Input with deprecated `error` & `hint` props"
|
|
137
148
|
hint="This is a helpful message"
|
|
138
149
|
error="Validation error, please take a look"
|
|
150
|
+
messageIconLabel={args.messageIconLabel}
|
|
139
151
|
>
|
|
140
152
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
141
153
|
</Field>
|
|
142
154
|
|
|
143
155
|
{/* instance of info via `message` property, this is rare case (e.g MoneyInput) */}
|
|
144
|
-
<Field
|
|
156
|
+
<Field
|
|
157
|
+
label="Text Input with Info under the field"
|
|
158
|
+
message="This is a helpful message"
|
|
159
|
+
messageIconLabel={args.messageIconLabel}
|
|
160
|
+
>
|
|
145
161
|
<Input value={value} onChange={({ target }) => setValue(target.value)} />
|
|
146
162
|
</Field>
|
|
147
163
|
</div>
|
package/src/field/Field.tsx
CHANGED
|
@@ -20,6 +20,11 @@ export type FieldProps = {
|
|
|
20
20
|
/** @deprecated use `description` prop instead */
|
|
21
21
|
hint?: React.ReactNode;
|
|
22
22
|
message?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Override for the [InlineAlert icon's default, accessible name](/?path=/docs/other-statusicon-accessibility--docs)
|
|
25
|
+
* announced by the screen readers
|
|
26
|
+
* */
|
|
27
|
+
messageIconLabel?: string;
|
|
23
28
|
description?: React.ReactNode;
|
|
24
29
|
/** @deprecated use `message` and `type={Sentiment.NEGATIVE}` prop instead */
|
|
25
30
|
error?: React.ReactNode;
|
|
@@ -33,6 +38,7 @@ export const Field = ({
|
|
|
33
38
|
label,
|
|
34
39
|
required = true,
|
|
35
40
|
message: propMessage,
|
|
41
|
+
messageIconLabel,
|
|
36
42
|
hint,
|
|
37
43
|
description = hint,
|
|
38
44
|
sentiment: propType = Sentiment.NEUTRAL,
|
|
@@ -97,7 +103,7 @@ export const Field = ({
|
|
|
97
103
|
)}
|
|
98
104
|
|
|
99
105
|
{message && (
|
|
100
|
-
<InlineAlert type={sentiment} id={messageId}>
|
|
106
|
+
<InlineAlert type={sentiment} id={messageId} iconLabel={messageIconLabel}>
|
|
101
107
|
{message}
|
|
102
108
|
</InlineAlert>
|
|
103
109
|
)}
|
package/src/i18n/en.json
CHANGED
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
"neptune.SelectInput.noResultsFound": "No results found",
|
|
28
28
|
"neptune.SelectOption.action.label": "Choose",
|
|
29
29
|
"neptune.SelectOption.selected.action.label": "Change chosen option",
|
|
30
|
+
"neptune.StatusIcon.iconLabel.error": "Error:",
|
|
31
|
+
"neptune.StatusIcon.iconLabel.information": "Information:",
|
|
32
|
+
"neptune.StatusIcon.iconLabel.pending": "Pending:",
|
|
33
|
+
"neptune.StatusIcon.iconLabel.success": "Success:",
|
|
34
|
+
"neptune.StatusIcon.iconLabel.warning": "Warning:",
|
|
30
35
|
"neptune.Summary.statusDone": "Item done",
|
|
31
36
|
"neptune.Summary.statusNotDone": "Item to do",
|
|
32
37
|
"neptune.Summary.statusPending": "Item pending",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
import { Sentiment } from '../common';
|
|
3
2
|
import { render, screen, mockMatchMedia } from '../test-utils';
|
|
4
3
|
|
|
@@ -81,4 +80,16 @@ describe('InlineAlert', () => {
|
|
|
81
80
|
expect(screen.getByTestId('status-icon')).toBeInTheDocument();
|
|
82
81
|
});
|
|
83
82
|
});
|
|
83
|
+
|
|
84
|
+
describe('other props', () => {
|
|
85
|
+
it('should respect `iconLabel` override', () => {
|
|
86
|
+
const customLabel = 'Custom Label';
|
|
87
|
+
render(
|
|
88
|
+
<InlineAlert type={Sentiment.WARNING} iconLabel={customLabel}>
|
|
89
|
+
{message}
|
|
90
|
+
</InlineAlert>,
|
|
91
|
+
);
|
|
92
|
+
expect(screen.getByLabelText(customLabel)).toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
84
95
|
});
|
|
@@ -8,6 +8,7 @@ import Body from '../body';
|
|
|
8
8
|
export interface InlineAlertProps {
|
|
9
9
|
id?: string;
|
|
10
10
|
type?: `${Sentiment}`;
|
|
11
|
+
iconLabel?: string;
|
|
11
12
|
className?: string;
|
|
12
13
|
children: ReactNode;
|
|
13
14
|
}
|
|
@@ -32,6 +33,7 @@ const iconTypes = new Set<NonNullable<InlineAlertProps['type']>>([
|
|
|
32
33
|
export default function InlineAlert({
|
|
33
34
|
id,
|
|
34
35
|
type = 'neutral',
|
|
36
|
+
iconLabel,
|
|
35
37
|
className,
|
|
36
38
|
children,
|
|
37
39
|
}: InlineAlertProps) {
|
|
@@ -46,7 +48,9 @@ export default function InlineAlert({
|
|
|
46
48
|
className,
|
|
47
49
|
)}
|
|
48
50
|
>
|
|
49
|
-
{iconTypes.has(type) &&
|
|
51
|
+
{iconTypes.has(type) && (
|
|
52
|
+
<StatusIcon sentiment={type} size={Size.SMALL} iconLabel={iconLabel} />
|
|
53
|
+
)}
|
|
50
54
|
<div>{children}</div>
|
|
51
55
|
</Body>
|
|
52
56
|
);
|
package/src/modal/Modal.spec.js
CHANGED
|
@@ -16,7 +16,7 @@ jest.mock('react-intl', () => ({
|
|
|
16
16
|
function (props) {
|
|
17
17
|
return <Component {...props} intl={{ locale: defaultLocale }} />;
|
|
18
18
|
},
|
|
19
|
-
useIntl: () => ({ locale: defaultLocale, formatMessage: (id) =>
|
|
19
|
+
useIntl: () => ({ locale: defaultLocale, formatMessage: (id) => String(id) }),
|
|
20
20
|
defineMessages: (translations) => translations,
|
|
21
21
|
}));
|
|
22
22
|
|
|
@@ -161,6 +161,24 @@ describe('Modal', () => {
|
|
|
161
161
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
+
it('passes onUnmount to Dimmer onExited prop', () => {
|
|
165
|
+
const onUnmount = jest.fn();
|
|
166
|
+
component.setProps({ onUnmount });
|
|
167
|
+
component.setProps({ open: true });
|
|
168
|
+
expect(component.find('Dimmer').prop('onExited')).toBe(onUnmount);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('calls onUnmount when the component unmounts', () => {
|
|
172
|
+
const onUnmount = jest.fn();
|
|
173
|
+
component.setProps({ onUnmount });
|
|
174
|
+
component.setProps({ open: true });
|
|
175
|
+
expect(onUnmount).not.toHaveBeenCalled();
|
|
176
|
+
jest.runAllTimers();
|
|
177
|
+
component.setProps({ open: false });
|
|
178
|
+
component.find('Dimmer').prop('onExited')();
|
|
179
|
+
expect(onUnmount).toHaveBeenCalledTimes(1);
|
|
180
|
+
});
|
|
181
|
+
|
|
164
182
|
const clickCloseButton = () => {
|
|
165
183
|
component.find('.close').simulate('click');
|
|
166
184
|
};
|
package/src/modal/Modal.tsx
CHANGED
|
@@ -32,6 +32,7 @@ export type ModalProps = CommonProps & {
|
|
|
32
32
|
footer?: ReactNode;
|
|
33
33
|
size?: SizeSmall | SizeMedium | SizeLarge | SizeExtraLarge;
|
|
34
34
|
onClose: () => void;
|
|
35
|
+
onUnmount?: () => void;
|
|
35
36
|
open: boolean;
|
|
36
37
|
scroll?: ScrollContent | ScrollViewport;
|
|
37
38
|
position?: PositionTop | PositionCenter;
|
|
@@ -43,6 +44,7 @@ const Modal = ({
|
|
|
43
44
|
body,
|
|
44
45
|
footer = null,
|
|
45
46
|
onClose,
|
|
47
|
+
onUnmount,
|
|
46
48
|
className,
|
|
47
49
|
open,
|
|
48
50
|
size = Size.MEDIUM,
|
|
@@ -71,6 +73,7 @@ const Modal = ({
|
|
|
71
73
|
footerContent={footer}
|
|
72
74
|
position={Position.BOTTOM}
|
|
73
75
|
onClose={onClose}
|
|
76
|
+
onUnmount={onUnmount}
|
|
74
77
|
>
|
|
75
78
|
{body}
|
|
76
79
|
</Drawer>
|
|
@@ -81,6 +84,7 @@ const Modal = ({
|
|
|
81
84
|
contentPosition={position}
|
|
82
85
|
disableClickToClose={disableDimmerClickToClose}
|
|
83
86
|
onClose={onClose}
|
|
87
|
+
onExited={onUnmount}
|
|
84
88
|
>
|
|
85
89
|
<CSSTransition
|
|
86
90
|
nodeRef={contentReference}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Meta, Source } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Other/StatusIcon/Accessibility" />
|
|
4
|
+
|
|
5
|
+
# Accessibility
|
|
6
|
+
|
|
7
|
+
By default, the component offers accessible names for all the icons, to ensure their meaning is conveyed to sighted and non-sighted users alike. The built-in labels are derived from the provided `sentiment` prop (as listed below), but can be overridden via `iconLabel` prop:
|
|
8
|
+
|
|
9
|
+
<Source code={`
|
|
10
|
+
Sentiment.NEGATIVE -> 'Error:'
|
|
11
|
+
Sentiment.POSITIVE -> 'Success:'
|
|
12
|
+
Sentiment.WARNING -> 'Warning:'
|
|
13
|
+
Sentiment.PENDING -> 'Pending:'
|
|
14
|
+
Sentiment.NEUTRAL -> 'Information:'
|
|
15
|
+
|
|
16
|
+
// deprecated
|
|
17
|
+
Sentiment.ERROR -> 'Error:'
|
|
18
|
+
Sentiment.INFO -> 'Information:'
|
|
19
|
+
Sentiment.SUCCESS -> 'Success:'
|
|
20
|
+
`} dark />
|
|
21
|
+
|
|
22
|
+
The purpose of the colon (`:`) is to make the screen reader briefly pause and separate the icon label from the rest of the content.
|
|
23
|
+
|
|
24
|
+
## Presentational icons
|
|
25
|
+
|
|
26
|
+
In some very rare cases, where the sentiment/status is already clearly communicated for **all** users (e.g. a duplicated icon in the `Upload` component), it might be more user-friendly to treat the `StatusIcon` as purely presentational.
|
|
27
|
+
|
|
28
|
+
To achieve it, simply set the `iconLabel` to `null` and that will result in `role="none" aria-hidden` applied to the icon.
|