@nypl/design-system-react-components 0.25.4 → 0.25.8

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/components/Breadcrumbs/BreadcrumbsTypes.d.ts +1 -0
  3. package/dist/components/Icons/IconSvgs.d.ts +1 -0
  4. package/dist/components/Icons/IconTypes.d.ts +1 -0
  5. package/dist/components/Image/ImageTypes.d.ts +3 -1
  6. package/dist/components/Link/LinkTypes.d.ts +1 -0
  7. package/dist/components/Notification/Notification.d.ts +2 -0
  8. package/dist/components/Select/Select.d.ts +2 -0
  9. package/dist/components/Toggle/Toggle.d.ts +47 -0
  10. package/dist/components/Toggle/ToggleSizes.d.ts +4 -0
  11. package/dist/components/VideoPlayer/VideoPlayer.d.ts +5 -3
  12. package/dist/design-system-react-components.cjs.development.js +921 -360
  13. package/dist/design-system-react-components.cjs.development.js.map +1 -1
  14. package/dist/design-system-react-components.cjs.production.min.js +1 -1
  15. package/dist/design-system-react-components.cjs.production.min.js.map +1 -1
  16. package/dist/design-system-react-components.esm.js +917 -353
  17. package/dist/design-system-react-components.esm.js.map +1 -1
  18. package/dist/index.d.ts +5 -0
  19. package/dist/theme/components/breadcrumb.d.ts +9 -0
  20. package/dist/theme/components/notification.d.ts +8 -4
  21. package/dist/theme/components/toggle.d.ts +68 -0
  22. package/dist/theme/foundations/spacing.d.ts +2 -0
  23. package/package.json +2 -2
  24. package/src/components/Accordion/Accordion.stories.mdx +1 -2
  25. package/src/components/Breadcrumbs/Breadcrumbs.stories.mdx +20 -3
  26. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +20 -0
  27. package/src/components/Breadcrumbs/BreadcrumbsTypes.tsx +1 -0
  28. package/src/components/Breadcrumbs/__snapshots__/Breadcrumbs.test.tsx.snap +198 -0
  29. package/src/components/Button/Button.tsx +0 -1
  30. package/src/components/Card/Card.stories.mdx +74 -7
  31. package/src/components/Card/Card.tsx +9 -8
  32. package/src/components/Card/__snapshots__/Card.test.tsx.snap +67 -35
  33. package/src/components/Chakra/Grid.stories.mdx +11 -14
  34. package/src/components/DatePicker/DatePicker.test.tsx +8 -6
  35. package/src/components/DatePicker/__snapshots__/DatePicker.test.tsx.snap +5 -4
  36. package/src/components/Icons/Icon.stories.mdx +3 -2
  37. package/src/components/Icons/IconSvgs.tsx +2 -0
  38. package/src/components/Icons/IconTypes.tsx +1 -0
  39. package/src/components/Image/Image.stories.mdx +152 -90
  40. package/src/components/Image/Image.test.tsx +10 -0
  41. package/src/components/Image/ImageTypes.ts +2 -0
  42. package/src/components/Image/__snapshots__/Image.test.tsx.snap +24 -8
  43. package/src/components/Label/Label.stories.mdx +1 -1
  44. package/src/components/Link/Link.stories.mdx +2 -3
  45. package/src/components/Link/Link.test.tsx +71 -0
  46. package/src/components/Link/Link.tsx +41 -9
  47. package/src/components/Link/LinkTypes.tsx +1 -0
  48. package/src/components/Link/__snapshots__/Link.test.tsx.snap +201 -0
  49. package/src/components/Notification/Notification.stories.mdx +36 -3
  50. package/src/components/Notification/Notification.test.tsx +62 -16
  51. package/src/components/Notification/Notification.tsx +17 -5
  52. package/src/components/Notification/__snapshots__/Notification.test.tsx.snap +117 -0
  53. package/src/components/Pagination/Pagination.stories.mdx +1 -2
  54. package/src/components/Pagination/__snapshots__/Pagination.test.tsx.snap +42 -0
  55. package/src/components/ProgressIndicator/ProgressIndicator.stories.mdx +2 -3
  56. package/src/components/SearchBar/SearchBar.Test.tsx +64 -20
  57. package/src/components/SearchBar/SearchBar.stories.mdx +3 -4
  58. package/src/components/SearchBar/SearchBar.tsx +4 -1
  59. package/src/components/Select/Select.stories.mdx +132 -55
  60. package/src/components/Select/Select.test.tsx +2 -2
  61. package/src/components/Select/Select.tsx +6 -2
  62. package/src/components/Slider/Slider.stories.mdx +3 -2
  63. package/src/components/Slider/Slider.test.tsx +35 -0
  64. package/src/components/Slider/Slider.tsx +8 -2
  65. package/src/components/Template/Template.stories.mdx +1 -2
  66. package/src/components/Toggle/Toggle.stories.mdx +176 -0
  67. package/src/components/Toggle/Toggle.test.tsx +140 -0
  68. package/src/components/Toggle/Toggle.tsx +118 -0
  69. package/src/components/Toggle/ToggleSizes.tsx +4 -0
  70. package/src/components/Toggle/__snapshots__/Toggle.test.tsx.snap +255 -0
  71. package/src/components/VideoPlayer/VideoPlayer.stories.mdx +39 -18
  72. package/src/components/VideoPlayer/VideoPlayer.test.tsx +103 -1
  73. package/src/components/VideoPlayer/VideoPlayer.tsx +57 -17
  74. package/src/components/VideoPlayer/__snapshots__/VideoPlayer.test.tsx.snap +48 -0
  75. package/src/index.ts +8 -0
  76. package/src/theme/components/breadcrumb.ts +11 -1
  77. package/src/theme/components/card.ts +4 -5
  78. package/src/theme/components/global.ts +1 -1
  79. package/src/theme/components/icon.ts +2 -2
  80. package/src/theme/components/image.ts +8 -0
  81. package/src/theme/components/notification.ts +8 -6
  82. package/src/theme/components/toggle.ts +65 -0
  83. package/src/theme/foundations/spacing.ts +3 -0
  84. package/src/theme/index.ts +2 -0
  85. package/src/utils/componentCategories.ts +3 -1
@@ -37,11 +37,13 @@ import { getCategory } from "../../utils/componentCategories";
37
37
  | Component Version | DS Version |
38
38
  | ----------------- | ---------- |
39
39
  | Added | `0.23.2` |
40
- | Latest | `0.25.2` |
40
+ | Latest | `0.25.8` |
41
41
 
42
42
  <Description of={VideoPlayer} />
43
43
 
44
- The `VideoPlayer` component is used to embed a `Vimeo` or `YouTube` video player on a page. The component requires both the `videoType` and `videoId` props. You can extract the videoId from the video's URL. **IMPORTANT:** Do not try to enter the full video URL as the `videoId` value.
44
+ The `VideoPlayer` component is used to embed a `Vimeo` or `YouTube` video player on a page. The component requires the `embedCode` prop or both the `videoType` and `videoId` props. The component will not function properly if you try to set all of them.
45
+
46
+ You can extract the videoId from the video's URL. **IMPORTANT:** Do not try to enter the full video URL as the `videoId` value.
45
47
 
46
48
  The `aspectRatio` prop is used to control the sizing of the space allotted for the video rendering. Ultimately, the `aspectRatio` prop should be set to match the aspect ratio of the video that is being rendered. The default aspect ratio is `16:9`.
47
49
 
@@ -49,27 +51,15 @@ The `aspectRatio` prop is used to control the sizing of the space allotted for t
49
51
  <Story
50
52
  name="VideoPlayer"
51
53
  args={{
52
- videoType: VideoPlayerTypes.Vimeo,
53
- videoId: "",
54
- headingText: "NYPL Video",
55
54
  descriptionText:
56
55
  "Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.",
56
+ embedCode: `<iframe src="https://player.vimeo.com/video/421404144?h=5467db7edd" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>`,
57
+ headingText: "NYPL Video",
57
58
  helperText:
58
59
  "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.",
59
60
  }}
60
61
  >
61
- {(args) => (
62
- <VideoPlayer
63
- {...args}
64
- videoType={args.videoType}
65
- videoId={
66
- args.videoType === "vimeo"
67
- ? args.videoId || "474719268"
68
- : args.videoId || "nm-dD2tx6bk"
69
- }
70
- aspectRatio={args.aspectRatio}
71
- />
72
- )}
62
+ {(args) => <VideoPlayer {...args} />}
73
63
  </Story>
74
64
  </Canvas>
75
65
 
@@ -94,6 +84,37 @@ https://www.youtube.com/watch?v=[VIDEO_ID]
94
84
  https://www.youtube.com/watch?v=nm-dD2tx6bk
95
85
  ```
96
86
 
87
+ ## Example Embed Code Snippets
88
+
89
+ The examples below are shown with precise HTML formatting. However, often the embed code snippets provided by 3rd party video services are delivered as one long string.
90
+
91
+ ### Vimeo
92
+
93
+ ```html
94
+ <iframe
95
+ src="https://player.vimeo.com/video/421404144?h=5467db7edd"
96
+ width="640"
97
+ height="360"
98
+ frameborder="0"
99
+ allow="autoplay; fullscreen; picture-in-picture"
100
+ allowfullscreen
101
+ ></iframe>
102
+ ```
103
+
104
+ ### YouTube
105
+
106
+ ```html
107
+ <iframe
108
+ width="560"
109
+ height="315"
110
+ src="https://www.youtube.com/embed/nm-dD2tx6bk"
111
+ title="YouTube video player"
112
+ frameborder="0"
113
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
114
+ allowfullscreen
115
+ ></iframe>
116
+ ```
117
+
97
118
  ## Errored
98
119
 
99
120
  If the necessary props are not passed to the `VideoPlayer` component, the component UI will display an error message.
@@ -101,8 +122,8 @@ If the necessary props are not passed to the `VideoPlayer` component, the compon
101
122
  <Canvas>
102
123
  <DSProvider>
103
124
  <VideoPlayer
104
- type={VideoPlayerTypes.Vimeo}
105
125
  videoId="https://vimeo.com/474719268"
126
+ videoType={VideoPlayerTypes.Vimeo}
106
127
  />
107
128
  </DSProvider>
108
129
  </Canvas>
@@ -63,6 +63,46 @@ describe("VideoPlayer", () => {
63
63
  });
64
64
  });
65
65
 
66
+ describe("using embedCode prop", () => {
67
+ const embedCode = `<iframe src="https://player.vimeo.com/video/421404144?h=5467db7edd" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>`;
68
+ const embedCodeWithTitle = `<iframe title="Pre-Existing iFrame Title" src="https://player.vimeo.com/video/421404144?h=5467db7edd" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>`;
69
+
70
+ it("Renders custom embed code", () => {
71
+ render(<VideoPlayer embedCode={embedCode} />);
72
+ expect(screen.getByTitle("Video player")).toBeInTheDocument();
73
+ expect(screen.getByTitle("Video player")).toHaveAttribute(
74
+ "src",
75
+ `https://player.vimeo.com/video/421404144?h=5467db7edd`
76
+ );
77
+ });
78
+
79
+ it("Renders custom embed code with custom iframe title", () => {
80
+ render(
81
+ <VideoPlayer embedCode={embedCode} iframeTitle="Custom iFrame Title" />
82
+ );
83
+ expect(screen.getByTitle("Custom iFrame Title")).toBeInTheDocument();
84
+ });
85
+
86
+ it("Renders custom embed code with pre-existing title", () => {
87
+ render(<VideoPlayer embedCode={embedCodeWithTitle} />);
88
+ expect(
89
+ screen.getByTitle("Pre-Existing iFrame Title")
90
+ ).toBeInTheDocument();
91
+ });
92
+
93
+ it("Renders custom embed code with pre-existing title and ignores `iframeTitle` prop", () => {
94
+ render(
95
+ <VideoPlayer
96
+ embedCode={embedCodeWithTitle}
97
+ iframeTitle="Custom iFrame Title"
98
+ />
99
+ );
100
+ expect(
101
+ screen.getByTitle("Pre-Existing iFrame Title")
102
+ ).toBeInTheDocument();
103
+ });
104
+ });
105
+
66
106
  describe("text elements", () => {
67
107
  beforeEach(() => {
68
108
  utils = render(
@@ -121,7 +161,7 @@ describe("VideoPlayer", () => {
121
161
  });
122
162
 
123
163
  describe("prop validation", () => {
124
- it("Throws error if videoId not formatted properly", () => {
164
+ it("Throws error if the videoId prop not is formatted properly", () => {
125
165
  render(
126
166
  <VideoPlayer
127
167
  videoType={VideoPlayerTypes.Vimeo}
@@ -135,6 +175,56 @@ describe("VideoPlayer", () => {
135
175
  )
136
176
  ).toBeInTheDocument();
137
177
  });
178
+
179
+ it("Throws appropriate error if VideoPlayer props are not configured properly", () => {
180
+ const warn = jest.spyOn(console, "warn");
181
+
182
+ render(<VideoPlayer />);
183
+ expect(warn).toHaveBeenCalledWith(
184
+ "VideoPlayer requires either the `embedCode` prop or both the `videoType` and `videoId` props."
185
+ );
186
+
187
+ render(<VideoPlayer videoId="http://vimeo.com/474719268" />);
188
+ expect(warn).toHaveBeenCalledWith(
189
+ "VideoPlayer also requires the `videoType` prop. You have only set the `videoId` prop."
190
+ );
191
+
192
+ render(<VideoPlayer videoType={VideoPlayerTypes.Vimeo} />);
193
+ expect(warn).toHaveBeenCalledWith(
194
+ "VideoPlayer also requires the `videoId` prop. You have only set the `videoType` prop."
195
+ );
196
+
197
+ render(
198
+ <VideoPlayer
199
+ embedCode="<iframe src='https://player.vimeo.com/video/421404144?h=5467db7edd' width='640' height='360' frameborder='0' allow='autoplay; fullscreen; picture-in-picture' allowfullscreen></iframe>"
200
+ videoType={VideoPlayerTypes.Vimeo}
201
+ videoId="http://vimeo.com/474719268"
202
+ />
203
+ );
204
+ expect(warn).toHaveBeenCalledWith(
205
+ "VideoPlayer can accept the `embedCode` prop or the `videoType` and `videoId` props. You have set both."
206
+ );
207
+
208
+ render(
209
+ <VideoPlayer
210
+ embedCode="<iframe src='https://player.vimeo.com/video/421404144?h=5467db7edd' width='640' height='360' frameborder='0' allow='autoplay; fullscreen; picture-in-picture' allowfullscreen></iframe>"
211
+ videoType={VideoPlayerTypes.Vimeo}
212
+ />
213
+ );
214
+ expect(warn).toHaveBeenCalledWith(
215
+ "VideoPlayer can accept the `embedCode` prop or the `videoType` and `videoId` props. You have set both."
216
+ );
217
+
218
+ render(
219
+ <VideoPlayer
220
+ embedCode="<iframe src='https://player.vimeo.com/video/421404144?h=5467db7edd' width='640' height='360' frameborder='0' allow='autoplay; fullscreen; picture-in-picture' allowfullscreen></iframe>"
221
+ videoId="http://vimeo.com/474719268"
222
+ />
223
+ );
224
+ expect(warn).toHaveBeenCalledWith(
225
+ "VideoPlayer can accept the `embedCode` prop or the `videoType` and `videoId` props. You have set both."
226
+ );
227
+ });
138
228
  });
139
229
 
140
230
  it("renders the UI snapshot correctly", () => {
@@ -159,6 +249,17 @@ describe("VideoPlayer", () => {
159
249
  />
160
250
  )
161
251
  .toJSON();
252
+ const videoPlayerUsingEmbedCode = renderer
253
+ .create(
254
+ <VideoPlayer
255
+ descriptionText="VideoPlayer description"
256
+ embedCode="<iframe src='https://player.vimeo.com/video/421404144?h=5467db7edd' width='640' height='360' frameborder='0' allow='autoplay; fullscreen; picture-in-picture' allowfullscreen></iframe>"
257
+ headingText="VideoPlayer Heading"
258
+ id="video-player-with-text"
259
+ helperText="VideoPlayer helper test."
260
+ />
261
+ )
262
+ .toJSON();
162
263
  const videoPlayerError = renderer
163
264
  .create(
164
265
  <VideoPlayer
@@ -171,6 +272,7 @@ describe("VideoPlayer", () => {
171
272
 
172
273
  expect(videoPlayerWithoutText).toMatchSnapshot();
173
274
  expect(videoPlayerWithText).toMatchSnapshot();
275
+ expect(videoPlayerUsingEmbedCode).toMatchSnapshot();
174
276
  expect(videoPlayerError).toMatchSnapshot();
175
277
  });
176
278
  });
@@ -12,20 +12,22 @@ export interface VideoPlayerProps {
12
12
  className?: string;
13
13
  /** Optional string to set the text for a video description */
14
14
  descriptionText?: string;
15
+ /** Optional string to set a code snippet provided by YouTube or Vimeo; the `videoPlayer` component will accept the `embedCode` prop or the `videoId` and `videoType` props */
16
+ embedCode?: string;
15
17
  /** Optional string to set the text for a `Heading` component */
16
18
  headingText?: string;
17
19
  /** Optional string to set the text for a `HelperErrorText` component */
18
20
  helperText?: string;
19
21
  /** ID that other components can cross reference for accessibility purposes */
20
22
  id?: string;
21
- /** Optional title to be added to the `<iframe>` element for improved accessibility; this title should describe in a few words the content of the video; if omitted, a generic title will be added */
23
+ /** Optional title to be added to the `<iframe>` element for improved accessibility; this title should describe in a few words the content of the video; if omitted, a generic title will be added; if a `title` attribute is already present in the `embedCode` prop, this prop will be ignored */
22
24
  iframeTitle?: string;
23
25
  /** Offers the ability to hide the helper/invalid text. */
24
26
  showHelperInvalidText?: boolean;
25
27
  /** Required YouTube or Vimeo video ID. This value can be pulled from a video's YouTube or Vimeo URL. */
26
- videoId: string;
28
+ videoId?: string;
27
29
  /** Required. Used to specify which video service is being used. */
28
- videoType: VideoPlayerTypes;
30
+ videoType?: VideoPlayerTypes;
29
31
  }
30
32
 
31
33
  export default function VideoPlayer(
@@ -35,6 +37,7 @@ export default function VideoPlayer(
35
37
  aspectRatio,
36
38
  className,
37
39
  descriptionText,
40
+ embedCode,
38
41
  headingText,
39
42
  helperText,
40
43
  id = generateUUID(),
@@ -54,29 +57,64 @@ export default function VideoPlayer(
54
57
  ? `https://player.vimeo.com/video/${videoId}?autoplay=0&loop=0`
55
58
  : `https://www.youtube.com/embed/${videoId}?disablekb=1&autoplay=0&fs=1&modestbranding=0`;
56
59
 
60
+ const iFrameTitleEmbedCode = iframeTitle ? `${iframeTitle}` : `Video player`;
61
+
62
+ const embedCodeFinal =
63
+ embedCode && embedCode.includes("<iframe") && !embedCode.includes("title=")
64
+ ? embedCode.replace(
65
+ `<iframe `,
66
+ `<iframe title="${iFrameTitleEmbedCode}" `
67
+ )
68
+ : embedCode;
69
+
57
70
  const errorMessage =
58
71
  "<strong>Error:</strong> This video player has not been configured properly. Please contact the site administrator.";
59
72
 
60
73
  let isInvalid = false;
61
- if (!videoType && !videoId) {
62
- console.warn("VideoPlayer requires the `videoType` and `videoId` props");
74
+ if (!embedCodeFinal && !videoType && !videoId) {
75
+ console.warn(
76
+ "VideoPlayer requires either the `embedCode` prop or both the `videoType` and `videoId` props."
77
+ );
78
+ isInvalid = true;
79
+ } else if (!embedCodeFinal && !videoType) {
80
+ console.warn(
81
+ "VideoPlayer also requires the `videoType` prop. You have only set the `videoId` prop."
82
+ );
83
+ isInvalid = true;
84
+ } else if (!embedCodeFinal && !videoId) {
85
+ console.warn(
86
+ "VideoPlayer also requires the `videoId` prop. You have only set the `videoType` prop."
87
+ );
63
88
  isInvalid = true;
64
- } else if (!videoType) {
65
- console.warn("VideoPlayer requires the `videoType` prop");
89
+ } else if (embedCodeFinal && (videoType || videoId)) {
90
+ console.warn(
91
+ "VideoPlayer can accept the `embedCode` prop or the `videoType` and `videoId` props. You have set both."
92
+ );
66
93
  isInvalid = true;
67
- } else if (!videoId) {
68
- console.warn("VideoPlayer requires the `videoId` prop");
94
+ }
95
+
96
+ if (
97
+ videoId &&
98
+ (videoId.includes("://") ||
99
+ videoId.includes("http") ||
100
+ videoId.includes(".") ||
101
+ videoId.includes("youtube") ||
102
+ videoId.includes("vimeo"))
103
+ ) {
104
+ console.warn("The VideoPlayer `videoId` prop is not configured properly.");
69
105
  isInvalid = true;
70
106
  }
71
107
 
72
108
  if (
73
- videoId.includes("://") ||
74
- videoId.includes("http") ||
75
- videoId.includes(".") ||
76
- videoId.includes("youtube") ||
77
- videoId.includes("vimeo")
109
+ embedCodeFinal &&
110
+ ((!embedCodeFinal.includes("vimeo.com") &&
111
+ !embedCodeFinal.includes("youtube.com")) ||
112
+ !embedCodeFinal.includes("<iframe") ||
113
+ !embedCodeFinal.includes("</iframe"))
78
114
  ) {
79
- console.warn("VideoPlayer `videoId` prop is not configured properly");
115
+ console.warn(
116
+ "The VideoPlayer `embedCode` prop is not configured properly."
117
+ );
80
118
  isInvalid = true;
81
119
  }
82
120
 
@@ -85,7 +123,9 @@ export default function VideoPlayer(
85
123
  : getVariant(aspectRatio, VideoPlayerAspectRatios);
86
124
  const styles = useMultiStyleConfig("VideoPlayer", { variant });
87
125
 
88
- const embedCode = (
126
+ const embedElement = embedCodeFinal ? (
127
+ <span dangerouslySetInnerHTML={{ __html: embedCodeFinal }} />
128
+ ) : (
89
129
  <Box
90
130
  as="iframe"
91
131
  src={videoSrc}
@@ -113,7 +153,7 @@ export default function VideoPlayer(
113
153
  helperText={helperText && showHelperInvalidText ? helperText : null}
114
154
  id={`${id}-componentWrapper`}
115
155
  >
116
- <Box __css={styles.inside}>{embedCode}</Box>
156
+ <Box __css={styles.inside}>{embedElement}</Box>
117
157
  </ComponentWrapper>
118
158
  )}
119
159
  </Box>
@@ -75,6 +75,54 @@ exports[`VideoPlayer renders the UI snapshot correctly 2`] = `
75
75
  `;
76
76
 
77
77
  exports[`VideoPlayer renders the UI snapshot correctly 3`] = `
78
+ <div
79
+ className="css-0"
80
+ data-testid="video-player-component"
81
+ id="video-player-with-text"
82
+ >
83
+ <div
84
+ className="css-0"
85
+ >
86
+ <h2
87
+ className="chakra-heading css-0"
88
+ id="video-player-with-text-componentWrapper-heading"
89
+ >
90
+ VideoPlayer Heading
91
+ </h2>
92
+ <p
93
+ className="chakra-text css-0"
94
+ >
95
+ VideoPlayer description
96
+ </p>
97
+ <div
98
+ className="css-0"
99
+ >
100
+ <span
101
+ dangerouslySetInnerHTML={
102
+ Object {
103
+ "__html": "<iframe title=\\"Video player\\" src='https://player.vimeo.com/video/421404144?h=5467db7edd' width='640' height='360' frameborder='0' allow='autoplay; fullscreen; picture-in-picture' allowfullscreen></iframe>",
104
+ }
105
+ }
106
+ />
107
+ </div>
108
+ <div
109
+ className="css-0"
110
+ >
111
+ <div
112
+ aria-atomic={true}
113
+ aria-live="off"
114
+ className=" css-0"
115
+ data-isinvalid={false}
116
+ id="video-player-with-text-componentWrapper-helperText"
117
+ >
118
+ VideoPlayer helper test.
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ `;
124
+
125
+ exports[`VideoPlayer renders the UI snapshot correctly 4`] = `
78
126
  <div
79
127
  className="css-0"
80
128
  data-testid="video-player-component"
package/src/index.ts CHANGED
@@ -73,6 +73,11 @@ export { default as Modal } from "./components/Modal/Modal";
73
73
  export { default as Notification } from "./components/Notification/Notification";
74
74
  export { NotificationTypes } from "./components/Notification/NotificationTypes";
75
75
  export { default as Pagination } from "./components/Pagination/Pagination";
76
+ export { default as ProgressIndicator } from "./components/ProgressIndicator/ProgressIndicator";
77
+ export {
78
+ ProgressIndicatorSizes,
79
+ ProgressIndicatorTypes,
80
+ } from "./components/ProgressIndicator/ProgressIndicatorTypes";
76
81
  export { default as Radio } from "./components/Radio/Radio";
77
82
  export { default as RadioGroup } from "./components/RadioGroup/RadioGroup";
78
83
  export { RadioGroupLayoutTypes } from "./components/RadioGroup/RadioGroupLayoutTypes";
@@ -84,6 +89,7 @@ export {
84
89
  SkeletonLoaderImageRatios,
85
90
  SkeletonLoaderLayouts,
86
91
  } from "./components/SkeletonLoader/SkeletonLoaderTypes";
92
+ export { default as Slider } from "./components/Slider/Slider";
87
93
  export { default as StatusBadge } from "./components/StatusBadge/StatusBadge";
88
94
  export { StatusBadgeTypes } from "./components/StatusBadge/StatusBadgeTypes";
89
95
  export {
@@ -111,6 +117,8 @@ export {
111
117
  TextInputRefType,
112
118
  } from "./components/TextInput/TextInput";
113
119
  export { TextInputTypes } from "./components/TextInput/TextInputTypes";
120
+ export { default as Toggle } from "./components/Toggle/Toggle";
121
+ export { ToggleSizes } from "./components/Toggle/ToggleSizes";
114
122
  export { default as useNYPLTheme } from "./hooks/useNYPLTheme";
115
123
  export { default as VideoPlayer } from "./components/VideoPlayer/VideoPlayer";
116
124
  export {
@@ -1,9 +1,18 @@
1
1
  // Variant styling
2
+ const blogs = {
3
+ bg: "section.blogs.secondary",
4
+ color: "ui.black",
5
+ a: {
6
+ _hover: {
7
+ color: "ui.gray.xdark",
8
+ },
9
+ },
10
+ };
2
11
  const booksAndMore = {
3
12
  bg: "section.books-and-more.secondary",
4
13
  };
5
14
  const locations = {
6
- bg: "section.locations.secondary",
15
+ bg: "section.locations.primary",
7
16
  };
8
17
  const research = {
9
18
  bg: "section.research.secondary",
@@ -67,6 +76,7 @@ const Breadcrumb = {
67
76
  },
68
77
  // Available variants:
69
78
  variants: {
79
+ blogs,
70
80
  booksAndMore,
71
81
  locations,
72
82
  research,
@@ -1,4 +1,6 @@
1
1
  const imageSizes = {
2
+ xxsmall: { flex: { base: "0 0 100%", md: "0 0 64px" }, width: "100%" },
3
+ xsmall: { flex: { md: "0 0 96px" } },
2
4
  small: { flex: { md: "0 0 165px" } },
3
5
  medium: { flex: { md: "0 0 225px" } },
4
6
  large: { flex: { md: "0 0 360px" } },
@@ -137,11 +139,6 @@ const CardImage = {
137
139
  const layoutStyles =
138
140
  layout === "row"
139
141
  ? {
140
- display: "flex",
141
- flexFlow: {
142
- base: "column nowrap",
143
- md: "row",
144
- },
145
142
  flex: { md: "0 0 225px" },
146
143
  maxWidth: { base: "100%", md: "50%" },
147
144
  textAlign: "left",
@@ -152,10 +149,12 @@ const CardImage = {
152
149
  ? "0 0 0 var(--nypl-space-m)"
153
150
  : "0 var(--nypl-space-m) 0 0",
154
151
  },
152
+ width: { base: "100%", md: null },
155
153
  ...size,
156
154
  }
157
155
  : {
158
156
  marginBottom: "xs",
157
+ width: "100%",
159
158
  };
160
159
  const imageAtEndStyles = imageAtEnd
161
160
  ? {
@@ -51,7 +51,7 @@ const checkboxRadioHelperStyle = {
51
51
  const labelLegendText = {
52
52
  alignItems: "baseline",
53
53
  width: "100%",
54
- marginBottom: "s",
54
+ marginBottom: "xs",
55
55
  fontSize: "14px",
56
56
  fontWeight: "medium",
57
57
  display: "flex",
@@ -6,8 +6,8 @@ const svgBase = {
6
6
  };
7
7
  const align = {
8
8
  none: {},
9
- left: { marginRight: "xs" },
10
- right: { marginLeft: "xs" },
9
+ left: { marginRight: "xxs" },
10
+ right: { marginLeft: "xxs" },
11
11
  };
12
12
  const iconRotation = {
13
13
  rotate180: {
@@ -12,6 +12,14 @@ const imageSizes = {
12
12
  default: {
13
13
  maxWidth: "100%",
14
14
  },
15
+ xxsmall: {
16
+ ...sideMarginsAuto,
17
+ maxWidth: "64px",
18
+ },
19
+ xsmall: {
20
+ ...sideMarginsAuto,
21
+ maxWidth: "96px",
22
+ },
15
23
  small: {
16
24
  ...sideMarginsAuto,
17
25
  maxWidth: "165px",
@@ -49,25 +49,26 @@ const Notification = {
49
49
 
50
50
  const NotificationContent = {
51
51
  parts: ["content"],
52
- baseStyle: ({ alignText, notificationType }) => ({
52
+ baseStyle: ({ alignText, icon, notificationType }) => ({
53
53
  display: "flex",
54
54
  justifyContent: "center",
55
55
  content: {
56
- width: "100%",
57
- paddingLeft: alignText
58
- ? "calc(var(--nypl-space-m) + var(--nypl-space-s))"
59
- : null,
60
56
  color:
61
57
  notificationType === NotificationTypes.Warning
62
58
  ? "brand.primary"
63
59
  : "currentColor",
60
+ marginTop: icon ? "xxxs" : "0",
61
+ paddingLeft: alignText
62
+ ? "calc(var(--nypl-space-m) + var(--nypl-space-s))"
63
+ : null,
64
+ width: "100%",
64
65
  },
65
66
  }),
66
67
  };
67
68
 
68
69
  const NotificationHeading = {
69
70
  parts: ["heading"],
70
- baseStyle: ({ centered, notificationType }) => {
71
+ baseStyle: ({ centered, icon, notificationType }) => {
71
72
  let color = "ui.black";
72
73
  if (notificationType === NotificationTypes.Announcement) {
73
74
  color = "section.research.secondary";
@@ -80,6 +81,7 @@ const NotificationHeading = {
80
81
  justifyContent: centered ? "center" : null,
81
82
  heading: {
82
83
  marginBottom: "0",
84
+ marginTop: icon ? "xxxs" : "0",
83
85
  color,
84
86
  },
85
87
  };
@@ -0,0 +1,65 @@
1
+ import { helperTextMargin } from "./global";
2
+
3
+ const baseStyle = {
4
+ label: { display: "flex", alignItems: "center", width: "fit-content" },
5
+ helper: {
6
+ ...helperTextMargin,
7
+ marginLeft: "xs",
8
+ },
9
+ };
10
+
11
+ const Switch = {
12
+ baseStyle: {
13
+ opacity: 0.4,
14
+ track: {
15
+ p: "4px",
16
+ border: "1px solid",
17
+ borderColor: "ui.gray.medium",
18
+ _checked: {
19
+ borderColor: "ui.link.primary",
20
+ bg: "ui.link.primary",
21
+ opacity: 1,
22
+ },
23
+ _invalid: {
24
+ borderColor: "ui.error.primary",
25
+ bg: "inherit",
26
+ "> span": {
27
+ bg: "ui.error.primary",
28
+ },
29
+ },
30
+ _disabled: {
31
+ borderColor: "ui.gray.medium",
32
+ bg: "ui.gray.medium",
33
+ _checked: {
34
+ opacity: 0.4,
35
+ },
36
+ },
37
+ _focus: {
38
+ outline: "2px solid",
39
+ outlineColor: "ui.focus",
40
+ outlineOffset: "2px",
41
+ zIndex: "9999",
42
+ },
43
+ },
44
+ label: { fontSize: -1, marginLeft: "xs" },
45
+ thumb: {
46
+ _disabled: {
47
+ bg: "ui.error.primary",
48
+ },
49
+ },
50
+ },
51
+ defaultProps: {
52
+ colorScheme: "white",
53
+ },
54
+ };
55
+
56
+ const Toggle = {
57
+ parts: ["helper"],
58
+ baseStyle,
59
+ // Default values
60
+ defaultProps: {
61
+ size: "lg",
62
+ },
63
+ };
64
+
65
+ export default { Toggle, Switch };
@@ -22,6 +22,7 @@
22
22
  *
23
23
  * Chakra Number Value | Chakra Name value | DS Variable
24
24
  * ------------------- | ----------------- | -----------------
25
+ * 0.5 | xxxs | --nypl-space-xxxs
25
26
  * 1 | xxs | --nypl-space-xxs
26
27
  * 2 | xs | --nypl-space-xs
27
28
  * 4 | s | --nypl-space-s
@@ -34,6 +35,8 @@
34
35
  export const spacing = {
35
36
  px: "1px",
36
37
  0: "0",
38
+ // --nypl-space-xxxs = 2px
39
+ xxxs: "0.125rem",
37
40
  0.5: "0.125rem",
38
41
  // --nypl-space-xxs = 4px
39
42
  xxs: "0.25rem",