@fpkit/acss 0.4.4
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/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/chunk-77CZU5XZ.cjs +9 -0
- package/dist/chunk-77CZU5XZ.cjs.map +1 -0
- package/dist/chunk-D43FJIRQ.cjs +31 -0
- package/dist/chunk-D43FJIRQ.cjs.map +1 -0
- package/dist/chunk-GJWMCDFS.js +9 -0
- package/dist/chunk-GJWMCDFS.js.map +1 -0
- package/dist/chunk-PCDUGD3C.js +5 -0
- package/dist/chunk-PCDUGD3C.js.map +1 -0
- package/dist/hooks.cjs +10 -0
- package/dist/hooks.cjs.map +1 -0
- package/dist/hooks.d.cts +32 -0
- package/dist/hooks.d.ts +32 -0
- package/dist/hooks.js +8 -0
- package/dist/hooks.js.map +1 -0
- package/dist/icon-e6044c73.d.ts +227 -0
- package/dist/icons.cjs +73 -0
- package/dist/icons.cjs.map +1 -0
- package/dist/icons.d.cts +252 -0
- package/dist/icons.d.ts +252 -0
- package/dist/icons.js +4 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.cjs +59 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +566 -0
- package/dist/index.d.ts +566 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/libs/chunk-GCGKYLDG.js +7 -0
- package/libs/chunk-GCGKYLDG.js.map +1 -0
- package/libs/chunk-PDD4N5P5.cjs +10 -0
- package/libs/chunk-PDD4N5P5.cjs.map +1 -0
- package/libs/chunk-QHIABQNQ.js +8 -0
- package/libs/chunk-QHIABQNQ.js.map +1 -0
- package/libs/chunk-ZOHIKF6I.cjs +31 -0
- package/libs/chunk-ZOHIKF6I.cjs.map +1 -0
- package/libs/components/badge/badge.css +1 -0
- package/libs/components/badge/badge.css.map +1 -0
- package/libs/components/badge/badge.min.css +3 -0
- package/libs/components/breadcrumbs/breadcrumb.css +1 -0
- package/libs/components/breadcrumbs/breadcrumb.css.map +1 -0
- package/libs/components/breadcrumbs/breadcrumb.min.css +3 -0
- package/libs/components/buttons/button.css +1 -0
- package/libs/components/buttons/button.css.map +1 -0
- package/libs/components/buttons/button.min.css +3 -0
- package/libs/components/cards/card-style.css +1 -0
- package/libs/components/cards/card-style.css.map +1 -0
- package/libs/components/cards/card-style.min.css +3 -0
- package/libs/components/cards/card.css +1 -0
- package/libs/components/cards/card.css.map +1 -0
- package/libs/components/cards/card.min.css +3 -0
- package/libs/components/details/details.css +1 -0
- package/libs/components/details/details.css.map +1 -0
- package/libs/components/details/details.min.css +3 -0
- package/libs/components/form/form.css +1 -0
- package/libs/components/form/form.css.map +1 -0
- package/libs/components/form/form.min.css +3 -0
- package/libs/components/icons/icon.css +1 -0
- package/libs/components/icons/icon.css.map +1 -0
- package/libs/components/icons/icon.min.css +3 -0
- package/libs/components/images/img.css +1 -0
- package/libs/components/images/img.css.map +1 -0
- package/libs/components/images/img.min.css +3 -0
- package/libs/components/layout/landmarks.css +1 -0
- package/libs/components/layout/landmarks.css.map +1 -0
- package/libs/components/layout/landmarks.min.css +3 -0
- package/libs/components/link/link.css +1 -0
- package/libs/components/link/link.css.map +1 -0
- package/libs/components/link/link.min.css +3 -0
- package/libs/components/nav/nav.css +1 -0
- package/libs/components/nav/nav.css.map +1 -0
- package/libs/components/nav/nav.min.css +3 -0
- package/libs/components/progress/progress.css +1 -0
- package/libs/components/progress/progress.css.map +1 -0
- package/libs/components/progress/progress.min.css +3 -0
- package/libs/components/styles/index.css +1 -0
- package/libs/components/styles/index.css.map +1 -0
- package/libs/components/styles/index.min.css +3 -0
- package/libs/components/tag/tag.css +1 -0
- package/libs/components/tag/tag.css.map +1 -0
- package/libs/components/tag/tag.min.css +3 -0
- package/libs/components/text-to-speech/text-to-speech.css +1 -0
- package/libs/components/text-to-speech/text-to-speech.css.map +1 -0
- package/libs/components/text-to-speech/text-to-speech.min.css +3 -0
- package/libs/hooks.cjs +12 -0
- package/libs/hooks.cjs.map +1 -0
- package/libs/hooks.d.cts +32 -0
- package/libs/hooks.d.ts +32 -0
- package/libs/hooks.js +3 -0
- package/libs/hooks.js.map +1 -0
- package/libs/icons-1f5afc0c.d.ts +318 -0
- package/libs/icons.cjs +12 -0
- package/libs/icons.cjs.map +1 -0
- package/libs/icons.d.cts +2 -0
- package/libs/icons.d.ts +2 -0
- package/libs/icons.js +3 -0
- package/libs/icons.js.map +1 -0
- package/libs/index.cjs +71 -0
- package/libs/index.cjs.map +1 -0
- package/libs/index.css +1 -0
- package/libs/index.css.map +1 -0
- package/libs/index.d.cts +551 -0
- package/libs/index.d.ts +551 -0
- package/libs/index.js +11 -0
- package/libs/index.js.map +1 -0
- package/package.json +125 -0
- package/src/App.css +42 -0
- package/src/App.tsx +35 -0
- package/src/__snapshots__/App.test.tsx.snap +56 -0
- package/src/components/.gitkeep +0 -0
- package/src/components/__snapshots__/fp.test.tsx.snap +3 -0
- package/src/components/badge/badge.scss +20 -0
- package/src/components/badge/badge.stories.tsx +54 -0
- package/src/components/badge/badge.tsx +17 -0
- package/src/components/breadcrumbs/bc-item.tsx +20 -0
- package/src/components/breadcrumbs/breadcrumb.scss +35 -0
- package/src/components/breadcrumbs/breadcrumb.stories.tsx +92 -0
- package/src/components/breadcrumbs/breadcrumb.tsx +218 -0
- package/src/components/buttons/button.scss +115 -0
- package/src/components/buttons/button.stories.tsx +57 -0
- package/src/components/buttons/button.test.tsx +104 -0
- package/src/components/buttons/button.tsx +64 -0
- package/src/components/cards/card-style.scss +0 -0
- package/src/components/cards/card.scss +43 -0
- package/src/components/cards/card.stories.tsx +114 -0
- package/src/components/cards/card.test.tsx +30 -0
- package/src/components/cards/card.tsx +135 -0
- package/src/components/cards/flex-card.tsx +15 -0
- package/src/components/details/details.scss +75 -0
- package/src/components/details/details.stories.tsx +122 -0
- package/src/components/details/details.tsx +77 -0
- package/src/components/form/README.mdx +70 -0
- package/src/components/form/fields.tsx +45 -0
- package/src/components/form/form.scss +87 -0
- package/src/components/form/form.stories.tsx +49 -0
- package/src/components/form/form.tsx +71 -0
- package/src/components/form/input.stories.tsx +155 -0
- package/src/components/form/inputs.tsx +84 -0
- package/src/components/form/select.stories.tsx +38 -0
- package/src/components/form/select.tsx +112 -0
- package/src/components/form/textarea.tsx +87 -0
- package/src/components/fp.test.tsx +56 -0
- package/src/components/fp.tsx +78 -0
- package/src/components/heading/heading.stories.tsx +75 -0
- package/src/components/heading/heading.tsx +27 -0
- package/src/components/icons/components/add.tsx +42 -0
- package/src/components/icons/components/arrow-down.tsx +52 -0
- package/src/components/icons/components/arrow-left.tsx +49 -0
- package/src/components/icons/components/arrow-right.tsx +52 -0
- package/src/components/icons/components/arrow-up.tsx +49 -0
- package/src/components/icons/components/chat.tsx +44 -0
- package/src/components/icons/components/code.tsx +50 -0
- package/src/components/icons/components/copy.tsx +51 -0
- package/src/components/icons/components/down.tsx +33 -0
- package/src/components/icons/components/home.tsx +57 -0
- package/src/components/icons/components/left.tsx +43 -0
- package/src/components/icons/components/minus.tsx +42 -0
- package/src/components/icons/components/pause-solid.tsx +48 -0
- package/src/components/icons/components/pause.tsx +63 -0
- package/src/components/icons/components/play-solid.tsx +44 -0
- package/src/components/icons/components/play.tsx +51 -0
- package/src/components/icons/components/remove.tsx +42 -0
- package/src/components/icons/components/resume-solid.tsx +52 -0
- package/src/components/icons/components/resume.tsx +57 -0
- package/src/components/icons/components/right.tsx +43 -0
- package/src/components/icons/components/star.tsx +38 -0
- package/src/components/icons/components/stop-solid.tsx +44 -0
- package/src/components/icons/components/stop.tsx +54 -0
- package/src/components/icons/components/svg.tsx +44 -0
- package/src/components/icons/components/up.tsx +31 -0
- package/src/components/icons/components/user.tsx +46 -0
- package/src/components/icons/icon.scss +15 -0
- package/src/components/icons/icon.stories.tsx +208 -0
- package/src/components/icons/icon.tsx +100 -0
- package/src/components/icons/index.ts +29 -0
- package/src/components/icons/types.ts +12 -0
- package/src/components/images/README.mdx +43 -0
- package/src/components/images/figure.stories.tsx +34 -0
- package/src/components/images/figure.tsx +44 -0
- package/src/components/images/img.scss +43 -0
- package/src/components/images/img.stories.tsx +24 -0
- package/src/components/images/img.test.tsx +43 -0
- package/src/components/images/img.tsx +93 -0
- package/src/components/images/place-holder.png +0 -0
- package/src/components/kit.tsx +56 -0
- package/src/components/layout/_header.scss +72 -0
- package/src/components/layout/footer.stories.tsx +34 -0
- package/src/components/layout/landmarks.scss +51 -0
- package/src/components/layout/landmarks.stories.tsx +54 -0
- package/src/components/layout/landmarks.tsx +149 -0
- package/src/components/layout/main.stories.tsx +90 -0
- package/src/components/link/link.scss +92 -0
- package/src/components/link/link.stories.tsx +74 -0
- package/src/components/link/link.tsx +48 -0
- package/src/components/list/list.stories.tsx +52 -0
- package/src/components/list/list.tsx +74 -0
- package/src/components/modal/dialog.tsx +50 -0
- package/src/components/modal/modal.tsx +85 -0
- package/src/components/nav/nav.scss +90 -0
- package/src/components/nav/nav.stories.tsx +96 -0
- package/src/components/nav/nav.tsx +76 -0
- package/src/components/popover/node_modules/.vitest/results.json +1 -0
- package/src/components/popover/popover.stories.tsx +31 -0
- package/src/components/popover/popover.test.tsx +39 -0
- package/src/components/popover/popover.tsx +85 -0
- package/src/components/progress/progress.scss +70 -0
- package/src/components/progress/progress.stories.tsx +51 -0
- package/src/components/progress/progress.tsx +82 -0
- package/src/components/readme.stories.mdx +7 -0
- package/src/components/styles/index.css +520 -0
- package/src/components/styles/index.css.map +1 -0
- package/src/components/tables/table-elements.tsx +57 -0
- package/src/components/tables/table.tsx +57 -0
- package/src/components/tag/tag.scss +56 -0
- package/src/components/tag/tag.stories.tsx +39 -0
- package/src/components/tag/tag.tsx +25 -0
- package/src/components/text/text.stories.tsx +67 -0
- package/src/components/text/text.tsx +93 -0
- package/src/components/text-to-speech/README.mdx +192 -0
- package/src/components/text-to-speech/TextInput.tsx +19 -0
- package/src/components/text-to-speech/TextToSpeech.stories.tsx +145 -0
- package/src/components/text-to-speech/TextToSpeech.tsx +94 -0
- package/src/components/text-to-speech/text-to-speech.scss +31 -0
- package/src/components/text-to-speech/useTextToSpeech.mdx +182 -0
- package/src/components/text-to-speech/useTextToSpeech.tsx +176 -0
- package/src/components/text-to-speech/views/TextToSpeechControls.tsx +117 -0
- package/src/components/ui.tsx +67 -0
- package/src/favicon.svg +15 -0
- package/src/hooks/popover/__snapshots__/popover.test.tsx.snap +88 -0
- package/src/hooks/popover/node_modules/.vitest/results.json +1 -0
- package/src/hooks/popover/popover.tsx +71 -0
- package/src/hooks/popover/use-popover.tsx +83 -0
- package/src/hooks.ts +1 -0
- package/src/icons.ts +1 -0
- package/src/index.css +13 -0
- package/src/index.scss +19 -0
- package/src/index.ts +35 -0
- package/src/libs/content.ts +30 -0
- package/src/logo.svg +7 -0
- package/src/main.tsx +10 -0
- package/src/patterns/.gitkeep +0 -0
- package/src/patterns/page/page-header.stories.tsx +44 -0
- package/src/patterns/page/page-header.tsx +78 -0
- package/src/sass/_elements.scss +17 -0
- package/src/sass/_globals.scss +162 -0
- package/src/sass/_layout.scss +51 -0
- package/src/sass/_loading-animation.scss +35 -0
- package/src/sass/_mixins.scss +10 -0
- package/src/sass/_properties.scss +106 -0
- package/src/sass/_reset.scss +183 -0
- package/src/sass/_type.scss +43 -0
- package/src/setupTest.ts +1 -0
- package/src/styles/badge/badge.css +22 -0
- package/src/styles/badge/badge.css.map +1 -0
- package/src/styles/breadcrumbs/breadcrumb.css +42 -0
- package/src/styles/breadcrumbs/breadcrumb.css.map +1 -0
- package/src/styles/buttons/button.css +93 -0
- package/src/styles/buttons/button.css.map +1 -0
- package/src/styles/cards/card-style.css +3 -0
- package/src/styles/cards/card-style.css.map +1 -0
- package/src/styles/cards/card.css +48 -0
- package/src/styles/cards/card.css.map +1 -0
- package/src/styles/details/details.css +69 -0
- package/src/styles/details/details.css.map +1 -0
- package/src/styles/dropdowns/dropdown.css.map +1 -0
- package/src/styles/form/form.css +93 -0
- package/src/styles/form/form.css.map +1 -0
- package/src/styles/form/style.css.map +1 -0
- package/src/styles/icons/icon.css +16 -0
- package/src/styles/icons/icon.css.map +1 -0
- package/src/styles/images/img.css +42 -0
- package/src/styles/images/img.css.map +1 -0
- package/src/styles/index.css +1330 -0
- package/src/styles/index.css.map +1 -0
- package/src/styles/layout/landmarks.css +155 -0
- package/src/styles/layout/landmarks.css.map +1 -0
- package/src/styles/link/link.css +88 -0
- package/src/styles/link/link.css.map +1 -0
- package/src/styles/nav/nav.css +85 -0
- package/src/styles/nav/nav.css.map +1 -0
- package/src/styles/progress/progress.css +54 -0
- package/src/styles/progress/progress.css.map +1 -0
- package/src/styles/progress/sass/progress.css.map +1 -0
- package/src/styles/styles/index.css +562 -0
- package/src/styles/styles/index.css.map +1 -0
- package/src/styles/tag/badge.css.map +1 -0
- package/src/styles/tag/tag.css +71 -0
- package/src/styles/tag/tag.css.map +1 -0
- package/src/styles/text-to-speech/text-to-speech.css +32 -0
- package/src/styles/text-to-speech/text-to-speech.css.map +1 -0
- package/src/test/setup.ts +6 -0
- package/src/types/component-props.ts +36 -0
- package/src/types/index.ts +2 -0
- package/src/types/input-props.ts +28 -0
- package/src/types/shared.ts +57 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Img } from './img'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
4
|
+
import jest from 'jest-mock'
|
|
5
|
+
describe('Img', () => {
|
|
6
|
+
it('should render an img element with passed props', () => {
|
|
7
|
+
const src = 'test.jpg'
|
|
8
|
+
const alt = 'Test image'
|
|
9
|
+
const width = 100
|
|
10
|
+
|
|
11
|
+
const { container } = render(<Img src={src} alt={alt} width={width} />)
|
|
12
|
+
|
|
13
|
+
const img = screen.getByRole('img')
|
|
14
|
+
expect(img).toBeInTheDocument()
|
|
15
|
+
expect(img).toHaveAttribute('src', src)
|
|
16
|
+
expect(img).toHaveAttribute('alt', alt)
|
|
17
|
+
expect(img).toHaveAttribute('width', width.toString())
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should apply default styles when renderStyles is true', () => {
|
|
21
|
+
const { container } = render(<Img src="" alt="" />)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should call imgError callback on error', () => {
|
|
25
|
+
const onError = jest.fn()
|
|
26
|
+
render(<Img src="bad.jpg" alt="" imgError={onError} />)
|
|
27
|
+
|
|
28
|
+
const img = screen.getByRole('img')
|
|
29
|
+
fireEvent.error(img)
|
|
30
|
+
|
|
31
|
+
expect(onError).toHaveBeenCalledTimes(1)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should call imgLoaded callback on load', () => {
|
|
35
|
+
const onLoad = jest.fn()
|
|
36
|
+
render(<Img src="good.jpg" alt="" imgLoaded={onLoad} />)
|
|
37
|
+
|
|
38
|
+
const img = screen.getByRole('img')
|
|
39
|
+
fireEvent.load(img)
|
|
40
|
+
|
|
41
|
+
expect(onLoad).toHaveBeenCalledTimes(1)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import UI from '../ui'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
/*
|
|
4
|
+
* ImageProps interface
|
|
5
|
+
*
|
|
6
|
+
* Extends ComponentProps and defines additional props for the Img component.
|
|
7
|
+
*
|
|
8
|
+
* @property {string} [src] - The image source URL
|
|
9
|
+
* @property {string} alt - Required alt text for image accessibility
|
|
10
|
+
* @property {number} width - Required width of image
|
|
11
|
+
* @property {number} [height] - Optional height of image
|
|
12
|
+
* @property {"eager" | "lazy"} [loading="lazy"] - Loading behavior
|
|
13
|
+
* @property {string} [placeholder] - Fallback placeholder image
|
|
14
|
+
* @property {"high" | "low"} [fetchpriority="low"] - Image fetch priority
|
|
15
|
+
* @property {"sync" | "async" | "auto"} [decoding="auto"] - Decode setting
|
|
16
|
+
* @property {function} [imgError] - Error callback
|
|
17
|
+
* @property {function} [imgLoaded] - Loaded callback
|
|
18
|
+
*/
|
|
19
|
+
export type ImageProps = React.ComponentProps<'img'> &
|
|
20
|
+
React.ComponentProps<typeof UI>
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Img component
|
|
24
|
+
*
|
|
25
|
+
* Renders an <img> element with custom props.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} src - The image source URL.
|
|
28
|
+
* @param {string} alt - The alt text for the image.
|
|
29
|
+
* @param {number} [width=480] - The width of the image.
|
|
30
|
+
* @param {number} [height] - The height of the image.
|
|
31
|
+
* @param {Object} [styles] - Additional CSS styles to apply.
|
|
32
|
+
* @param {boolean} [renderStyles=true] - Whether to render the default styles.
|
|
33
|
+
* @param {"eager" | "lazy"} [loading="lazy"] - The loading attribute.
|
|
34
|
+
* @param {string} [placeholder] - A placeholder image URL.
|
|
35
|
+
* @param {"high" | "low"} [fetchpriority="low"] - The fetchpriority attribute.
|
|
36
|
+
* @param {"sync" | "async" | "auto"} [decoding="auto"] - The decoding attribute.
|
|
37
|
+
* @param {function} [imgLoaded] - Callback when image loads successfully.
|
|
38
|
+
* @param {function} [imgError] - Callback when image errors.
|
|
39
|
+
*
|
|
40
|
+
* @returns {JSX.Element} The Img component.
|
|
41
|
+
*/
|
|
42
|
+
export const Img = ({
|
|
43
|
+
src = '//',
|
|
44
|
+
alt,
|
|
45
|
+
width = 480,
|
|
46
|
+
height,
|
|
47
|
+
styles,
|
|
48
|
+
loading = 'lazy',
|
|
49
|
+
placeholder = `https://via.placeholder.com/${width}?text=PLACEHOLDER`,
|
|
50
|
+
fetchpriority = 'low',
|
|
51
|
+
decoding = 'auto',
|
|
52
|
+
imgLoaded,
|
|
53
|
+
imgError,
|
|
54
|
+
...props
|
|
55
|
+
}: ImageProps) => {
|
|
56
|
+
const handleImgError = (
|
|
57
|
+
e: React.SyntheticEvent<HTMLImageElement, Event>,
|
|
58
|
+
): void => {
|
|
59
|
+
if (imgError) {
|
|
60
|
+
imgError?.(e)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if (e.currentTarget.src !== placeholder) {
|
|
64
|
+
e.currentTarget.src = placeholder
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const handleImgLoad = (
|
|
69
|
+
e: React.SyntheticEvent<HTMLImageElement, Event>,
|
|
70
|
+
): void => {
|
|
71
|
+
imgLoaded?.(e)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<UI
|
|
76
|
+
as="img"
|
|
77
|
+
src={src}
|
|
78
|
+
alt={alt}
|
|
79
|
+
width={width}
|
|
80
|
+
height={height || 'auto'}
|
|
81
|
+
loading={loading}
|
|
82
|
+
style={styles}
|
|
83
|
+
onError={handleImgError}
|
|
84
|
+
onLoad={handleImgLoad}
|
|
85
|
+
fetchPriority={fetchpriority}
|
|
86
|
+
decoding={decoding}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default Img
|
|
93
|
+
Img.displayName = 'Img'
|
|
Binary file
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
type Rainbow =
|
|
4
|
+
| 'red'
|
|
5
|
+
| 'orange'
|
|
6
|
+
| 'yellow'
|
|
7
|
+
| 'green'
|
|
8
|
+
| 'blue'
|
|
9
|
+
| 'indigo'
|
|
10
|
+
| 'violet'
|
|
11
|
+
|
|
12
|
+
type PolymorphicRef<C extends React.ElementType> =
|
|
13
|
+
React.ComponentPropsWithRef<C>['ref']
|
|
14
|
+
|
|
15
|
+
type AsProp<C extends React.ElementType> = {
|
|
16
|
+
as?: C
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P)
|
|
20
|
+
|
|
21
|
+
type PolymorphicComponentProp<
|
|
22
|
+
C extends React.ElementType,
|
|
23
|
+
Props = {},
|
|
24
|
+
> = React.PropsWithChildren<Props & AsProp<C>> &
|
|
25
|
+
Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>
|
|
26
|
+
|
|
27
|
+
type PolymorphicComponentPropWithRef<
|
|
28
|
+
C extends React.ElementType,
|
|
29
|
+
Props = {},
|
|
30
|
+
> = PolymorphicComponentProp<C, Props> & { ref?: PolymorphicRef<C> }
|
|
31
|
+
|
|
32
|
+
type TextProps<C extends React.ElementType> = PolymorphicComponentPropWithRef<
|
|
33
|
+
C,
|
|
34
|
+
{ color?: Rainbow | 'black' }
|
|
35
|
+
>
|
|
36
|
+
|
|
37
|
+
type TextComponent = <C extends React.ElementType = 'span'>(
|
|
38
|
+
props: TextProps<C>,
|
|
39
|
+
) => React.ReactElement | any
|
|
40
|
+
|
|
41
|
+
export const Text: TextComponent = React.forwardRef(
|
|
42
|
+
<C extends React.ElementType = 'span'>(
|
|
43
|
+
{ as, color, children }: TextProps<C>,
|
|
44
|
+
ref?: PolymorphicRef<C>,
|
|
45
|
+
) => {
|
|
46
|
+
const Component = as || 'span'
|
|
47
|
+
|
|
48
|
+
const style = color ? { style: { color } } : {}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Component {...style} ref={ref}>
|
|
52
|
+
{children}
|
|
53
|
+
</Component>
|
|
54
|
+
)
|
|
55
|
+
},
|
|
56
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
header,
|
|
2
|
+
[data-hero],
|
|
3
|
+
[data-grid~='overlay'] {
|
|
4
|
+
// Name of the grid area
|
|
5
|
+
--overlay-grid-area: overlay;
|
|
6
|
+
// Placement of items in grid area
|
|
7
|
+
--overlay-placement: center;
|
|
8
|
+
// Controls display of grid
|
|
9
|
+
--overlay-display: grid;
|
|
10
|
+
// Overlay padding
|
|
11
|
+
--overlay-padding: 2rem;
|
|
12
|
+
--overlay-w: 100%;
|
|
13
|
+
--overlay-h: 40vh;
|
|
14
|
+
--overlay-max-h: 500px;
|
|
15
|
+
--overlay-color: currentColor;
|
|
16
|
+
--overlay-content-w: 80%;
|
|
17
|
+
--overlay-gap: 2rem;
|
|
18
|
+
--overlay-bg: whitesmoke;
|
|
19
|
+
--overlay-px: auto;
|
|
20
|
+
--overlay-py: auto;
|
|
21
|
+
--overlay-mx: auto;
|
|
22
|
+
--overlay-my: auto;
|
|
23
|
+
|
|
24
|
+
grid-template-areas: 'overlay';
|
|
25
|
+
display: var(--overlay-display);
|
|
26
|
+
place-items: var(--overlay-placement);
|
|
27
|
+
align-items: var(--overlay-placement);
|
|
28
|
+
min-height: var(--overlay-h);
|
|
29
|
+
width: var(--overlay-w);
|
|
30
|
+
color: var(--overlay-color);
|
|
31
|
+
background-color: var(--overlay-bg);
|
|
32
|
+
min-width: 20rem;
|
|
33
|
+
> * {
|
|
34
|
+
grid-area: overlay;
|
|
35
|
+
}
|
|
36
|
+
> img {
|
|
37
|
+
width: var(--overlay-w);
|
|
38
|
+
// height: auto;
|
|
39
|
+
background-size: contain;
|
|
40
|
+
}
|
|
41
|
+
> div,
|
|
42
|
+
> section {
|
|
43
|
+
--overlay-display: flex;
|
|
44
|
+
max-width: var(--overlay-content-w);
|
|
45
|
+
padding-inline: var(--spc-4);
|
|
46
|
+
margin-inline: var(--overlay-mx);
|
|
47
|
+
gap: var(--overlay-gap);
|
|
48
|
+
text-align: center;
|
|
49
|
+
p {
|
|
50
|
+
width: auto;
|
|
51
|
+
max-width: 60ch;
|
|
52
|
+
font-size: var(--fs-8);
|
|
53
|
+
line-height: 1.4;
|
|
54
|
+
}
|
|
55
|
+
> h1,
|
|
56
|
+
> h2 {
|
|
57
|
+
line-height: var(--header-lh, 1.1);
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
> h1 {
|
|
62
|
+
font-size: var(--fs-12);
|
|
63
|
+
}
|
|
64
|
+
> h2 {
|
|
65
|
+
font-size: var(--fs-11);
|
|
66
|
+
}
|
|
67
|
+
> h3 {
|
|
68
|
+
font-size: var(--fs-10);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { StoryObj, Meta } from '@storybook/react'
|
|
2
|
+
/**
|
|
3
|
+
* Import testing library dependencies
|
|
4
|
+
*/
|
|
5
|
+
import { within, userEvent } from '@storybook/testing-library'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Import jest matchers
|
|
9
|
+
*/
|
|
10
|
+
import { expect } from '@storybook/jest'
|
|
11
|
+
|
|
12
|
+
import { Footer } from './landmarks'
|
|
13
|
+
|
|
14
|
+
const meta: Meta<typeof Footer> = {
|
|
15
|
+
title: 'FP.React Components/Layout/Landmarks',
|
|
16
|
+
component: Footer,
|
|
17
|
+
args: {
|
|
18
|
+
children: 'Main Landmark',
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
'data-testid': 'main',
|
|
21
|
+
},
|
|
22
|
+
} as Meta
|
|
23
|
+
|
|
24
|
+
const mainChildren = () => (
|
|
25
|
+
<>
|
|
26
|
+
<h2>Header Title</h2>
|
|
27
|
+
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero, unde?</p>
|
|
28
|
+
</>
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
export default meta
|
|
32
|
+
type Story = StoryObj<typeof Footer>
|
|
33
|
+
|
|
34
|
+
export const BasicFooter: Story = {}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
@use './header';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
main,
|
|
5
|
+
footer {
|
|
6
|
+
--content-w: min(100%, 1480px);
|
|
7
|
+
--content-mx: auto;
|
|
8
|
+
--content-px: 1rem;
|
|
9
|
+
--content-gap: 2rem;
|
|
10
|
+
padding-block: var(--overlay-padding);
|
|
11
|
+
> section {
|
|
12
|
+
width: var(--content-w);
|
|
13
|
+
margin-inline: var(--content-mx);
|
|
14
|
+
padding-inline: var(--spc-6);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
main {
|
|
19
|
+
flex: 1;
|
|
20
|
+
font-size: var(--fs-3);
|
|
21
|
+
> section[aria-label],
|
|
22
|
+
> section {
|
|
23
|
+
width: var(--content-w);
|
|
24
|
+
margin-inline: var(--content-mx);
|
|
25
|
+
&:has(> article, > aside) {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
flex: 1;
|
|
29
|
+
gap: var(--content-gap);
|
|
30
|
+
> article {
|
|
31
|
+
flex-basis: 0;
|
|
32
|
+
flex-grow: 999;
|
|
33
|
+
min-inline-size: 50%;
|
|
34
|
+
}
|
|
35
|
+
> aside {
|
|
36
|
+
flex-basis: 20rem;
|
|
37
|
+
flex-grow: 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
footer {
|
|
44
|
+
> div {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
min-height: 5rem;
|
|
49
|
+
text-align: center;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { StoryObj, Meta } from '@storybook/react'
|
|
2
|
+
/**
|
|
3
|
+
* Import testing library dependencies
|
|
4
|
+
*/
|
|
5
|
+
import { within, userEvent } from '@storybook/testing-library'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Import jest matchers
|
|
9
|
+
*/
|
|
10
|
+
import { expect } from '@storybook/jest'
|
|
11
|
+
|
|
12
|
+
import { Header } from './landmarks'
|
|
13
|
+
|
|
14
|
+
import Img from '#components/images/img'
|
|
15
|
+
|
|
16
|
+
const meta: Meta<typeof Header> = {
|
|
17
|
+
title: 'FP.React Components/Layout/Landmarks',
|
|
18
|
+
component: Header,
|
|
19
|
+
args: {
|
|
20
|
+
children: 'Default Header',
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
'data-testid': 'banner',
|
|
23
|
+
},
|
|
24
|
+
} as Meta
|
|
25
|
+
|
|
26
|
+
const headerChildren = () => (
|
|
27
|
+
<>
|
|
28
|
+
<h2>Header Title</h2>
|
|
29
|
+
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero, unde?</p>
|
|
30
|
+
</>
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
export default meta
|
|
34
|
+
type Story = StoryObj<typeof Header>
|
|
35
|
+
|
|
36
|
+
export const LandmarkDefault: Story = {}
|
|
37
|
+
|
|
38
|
+
export const HeroHeader: Story = {
|
|
39
|
+
args: {
|
|
40
|
+
children: headerChildren(),
|
|
41
|
+
headerBackground: <Img src="https://picsum.photos/2000/1000" alt="" />,
|
|
42
|
+
styles: { color: 'red' },
|
|
43
|
+
classNames: 'header-class',
|
|
44
|
+
'data-styles': 'blue',
|
|
45
|
+
},
|
|
46
|
+
play: async ({ canvasElement }) => {
|
|
47
|
+
const canvas = within(canvasElement)
|
|
48
|
+
const header = canvas.getByRole('banner')
|
|
49
|
+
expect(header).toBeInTheDocument()
|
|
50
|
+
const title = canvas.getByRole('heading')
|
|
51
|
+
expect(title).toBeInTheDocument()
|
|
52
|
+
expect(title).toHaveTextContent(/header title/i)
|
|
53
|
+
},
|
|
54
|
+
} as Story
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import UI from '../ui'
|
|
2
|
+
import React, { ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
type ComponentProps = React.ComponentProps<typeof UI>
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Renders children elements without any wrapping component.
|
|
8
|
+
* Can be used as a placeholder when no semantic landmark is needed.
|
|
9
|
+
*/
|
|
10
|
+
export const Landmarks = (children?: React.FC) => <>{children}</>
|
|
11
|
+
|
|
12
|
+
type HeaderProps = {
|
|
13
|
+
headerBackground?: ReactNode
|
|
14
|
+
} & ComponentProps
|
|
15
|
+
/**
|
|
16
|
+
* Header component.
|
|
17
|
+
*
|
|
18
|
+
* Renders a header landmark with a section child.
|
|
19
|
+
*
|
|
20
|
+
* @param children - The content to render inside the header.
|
|
21
|
+
* @param styles - Optional styles object.
|
|
22
|
+
* @param props - Other props.
|
|
23
|
+
*/
|
|
24
|
+
export const Header = ({
|
|
25
|
+
id,
|
|
26
|
+
children,
|
|
27
|
+
headerBackground,
|
|
28
|
+
styles,
|
|
29
|
+
classes,
|
|
30
|
+
...props
|
|
31
|
+
}: HeaderProps) => {
|
|
32
|
+
return (
|
|
33
|
+
<UI as="header" id={id} styles={styles} className={classes} {...props}>
|
|
34
|
+
{headerBackground}
|
|
35
|
+
<UI as="section">{children}</UI>
|
|
36
|
+
</UI>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Main component.
|
|
42
|
+
*
|
|
43
|
+
* Renders a main landmark.
|
|
44
|
+
*
|
|
45
|
+
* @param children - The content to render inside the main element.
|
|
46
|
+
* @param styles - Optional styles object.
|
|
47
|
+
* @param props - Other props.
|
|
48
|
+
*/
|
|
49
|
+
export const Main = ({
|
|
50
|
+
id,
|
|
51
|
+
children,
|
|
52
|
+
styles,
|
|
53
|
+
classes,
|
|
54
|
+
...props
|
|
55
|
+
}: ComponentProps) => {
|
|
56
|
+
return (
|
|
57
|
+
<UI as="main" id={id} styles={styles} {...props} className={classes}>
|
|
58
|
+
{children}
|
|
59
|
+
</UI>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Footer component that renders a footer element with a section element inside.
|
|
65
|
+
* @param {ReactNode} children - Child elements to render inside the section element.
|
|
66
|
+
* @param styles - CSS styles to apply to the footer element.
|
|
67
|
+
* @param props - Additional props to pass to the footer element.
|
|
68
|
+
* @returns A React component that renders a footer element with a section element inside.
|
|
69
|
+
*/
|
|
70
|
+
export const Footer = ({
|
|
71
|
+
id,
|
|
72
|
+
classes,
|
|
73
|
+
children,
|
|
74
|
+
styles = {},
|
|
75
|
+
...props
|
|
76
|
+
}: ComponentProps) => {
|
|
77
|
+
return (
|
|
78
|
+
<UI as="footer" id={id} className={classes} styles={styles} {...props}>
|
|
79
|
+
<UI as="section">{children || 'Copyright © 2022'}</UI>
|
|
80
|
+
</UI>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const Aside = ({
|
|
85
|
+
id,
|
|
86
|
+
children,
|
|
87
|
+
styles = {},
|
|
88
|
+
classes,
|
|
89
|
+
...props
|
|
90
|
+
}: ComponentProps) => {
|
|
91
|
+
return (
|
|
92
|
+
<UI as="aside" id={id} styles={styles} className={classes} {...props}>
|
|
93
|
+
<UI as="section">{children}</UI>
|
|
94
|
+
</UI>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Section component that renders a section element.
|
|
100
|
+
*
|
|
101
|
+
* @param children - Child elements to render inside the section.
|
|
102
|
+
* @param styles - CSS styles to apply to the section.
|
|
103
|
+
* @param props - Other props.
|
|
104
|
+
*/
|
|
105
|
+
export const Section = ({
|
|
106
|
+
id,
|
|
107
|
+
children,
|
|
108
|
+
styles,
|
|
109
|
+
classes,
|
|
110
|
+
...props
|
|
111
|
+
}: ComponentProps) => {
|
|
112
|
+
return (
|
|
113
|
+
<UI as="section" id={id} styles={styles} className={classes} {...props}>
|
|
114
|
+
{children}
|
|
115
|
+
</UI>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Article component renders an HTML <article> element.
|
|
121
|
+
*
|
|
122
|
+
* @param children - Child elements to render inside the article.
|
|
123
|
+
* @param styles - CSS styles to apply to the article.
|
|
124
|
+
* @param props - Additional props to pass to the article element.
|
|
125
|
+
*/
|
|
126
|
+
export const Article = ({
|
|
127
|
+
id,
|
|
128
|
+
children,
|
|
129
|
+
|
|
130
|
+
styles,
|
|
131
|
+
classes,
|
|
132
|
+
...props
|
|
133
|
+
}: ComponentProps) => {
|
|
134
|
+
return (
|
|
135
|
+
<UI as="article" id={id} styles={styles} className={classes} {...props}>
|
|
136
|
+
{children}
|
|
137
|
+
</UI>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default Landmarks
|
|
142
|
+
|
|
143
|
+
Landmarks.displayName = 'Landmarks'
|
|
144
|
+
Landmarks.Header = Header
|
|
145
|
+
Landmarks.Main = Main
|
|
146
|
+
Landmarks.Footer = Footer
|
|
147
|
+
Landmarks.Aside = Aside
|
|
148
|
+
Landmarks.Section = Section
|
|
149
|
+
Landmarks.Article = Article
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { StoryObj, Meta } from '@storybook/react'
|
|
2
|
+
/**
|
|
3
|
+
* Import testing library dependencies
|
|
4
|
+
*/
|
|
5
|
+
import { within, userEvent } from '@storybook/testing-library'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Import jest matchers
|
|
9
|
+
*/
|
|
10
|
+
import { expect } from '@storybook/jest'
|
|
11
|
+
|
|
12
|
+
import { Main } from './landmarks'
|
|
13
|
+
|
|
14
|
+
const meta: Meta<typeof Main> = {
|
|
15
|
+
title: 'FP.React Components/Layout/Landmarks',
|
|
16
|
+
component: Main,
|
|
17
|
+
args: {
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
children: (
|
|
20
|
+
<section>
|
|
21
|
+
The main HTML element represents the dominant content of the body of a
|
|
22
|
+
document.
|
|
23
|
+
</section>
|
|
24
|
+
),
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
'data-testid': 'main',
|
|
27
|
+
},
|
|
28
|
+
decorators: [
|
|
29
|
+
(Story) => (
|
|
30
|
+
<div style={{ minHeight: '80vh', display: 'flex' }}>
|
|
31
|
+
<Story />
|
|
32
|
+
</div>
|
|
33
|
+
),
|
|
34
|
+
],
|
|
35
|
+
} as Meta
|
|
36
|
+
|
|
37
|
+
const mainChildren = () => (
|
|
38
|
+
<>
|
|
39
|
+
<section aria-label="main-content">
|
|
40
|
+
<article>
|
|
41
|
+
<h3>Header Title</h3>
|
|
42
|
+
<p>
|
|
43
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis alias,
|
|
44
|
+
labore quibusdam, culpa dolorum rerum fugiat laborum deserunt sed ad
|
|
45
|
+
eveniet, modi reprehenderit vero pariatur enim esse eaque consectetur
|
|
46
|
+
nulla.
|
|
47
|
+
</p>
|
|
48
|
+
<hr />
|
|
49
|
+
<p>
|
|
50
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis alias,
|
|
51
|
+
labore quibusdam, culpa dolorum rerum fugiat laborum deserunt sed ad
|
|
52
|
+
eveniet, modi reprehenderit vero pariatur enim esse eaque consectetur
|
|
53
|
+
nulla.
|
|
54
|
+
</p>
|
|
55
|
+
<div>Lorem ipsum dolor sit amet.</div>
|
|
56
|
+
</article>
|
|
57
|
+
<aside>
|
|
58
|
+
<p>
|
|
59
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero, unde?
|
|
60
|
+
</p>
|
|
61
|
+
</aside>
|
|
62
|
+
</section>
|
|
63
|
+
</>
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
export default meta
|
|
67
|
+
type Story = StoryObj<typeof Main>
|
|
68
|
+
|
|
69
|
+
export const MainLandmark: Story = {
|
|
70
|
+
play: async ({ canvasElement }) => {
|
|
71
|
+
const canvas = within(canvasElement)
|
|
72
|
+
const main = canvas.getByRole('main')
|
|
73
|
+
expect(main).toBeInTheDocument()
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const MainArticles: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
// @ts-ignore
|
|
80
|
+
children: mainChildren(),
|
|
81
|
+
},
|
|
82
|
+
play: async ({ canvasElement }) => {
|
|
83
|
+
const canvas = within(canvasElement)
|
|
84
|
+
const main = canvas.getByRole('main')
|
|
85
|
+
expect(main).toBeInTheDocument()
|
|
86
|
+
const title = canvas.getByRole('heading')
|
|
87
|
+
expect(title).toBeInTheDocument()
|
|
88
|
+
expect(title).toHaveTextContent('Header Title')
|
|
89
|
+
},
|
|
90
|
+
}
|