@sproutsocial/seeds-react-table 1.0.50 → 1.0.51

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.
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Hybrid v2 Table components.
3
+ *
4
+ * Each exported sub-component renders the fast pure-CSS-class (Tailwind) path by
5
+ * default and transparently falls back to the styled-components path via the
6
+ * documented `needsStyledComponents` single gate — i.e. when a consumer passes
7
+ * styled-system props (px, color, width, …) or extends the component with
8
+ * `styled()` (detected through an injected `theme` prop or the generated
9
+ * `sc-XXXX` className). This mirrors the established Button/Badge hybrid pattern
10
+ * and preserves 100% backward compatibility with the existing styled-components
11
+ * API.
12
+ */
13
+
14
+ import React, { useId } from "react";
15
+ import { type TableV2Props } from "./TableV2Types";
16
+ import { needsStyledComponents } from "@sproutsocial/seeds-react-system-props";
17
+ import {
18
+ StyledTableV2Container,
19
+ StyledTableV2,
20
+ StyledCaption,
21
+ StyledTableHeader,
22
+ StyledTableBody,
23
+ StyledTableRow,
24
+ StyledTableHeaderCell,
25
+ StyledTableCell,
26
+ } from "./styles";
27
+ import {
28
+ TableV2ContainerTailwind,
29
+ TableRootTailwind,
30
+ CaptionTailwind,
31
+ TableHeaderTailwind,
32
+ TableBodyTailwind,
33
+ TableRowTailwind,
34
+ TableHeaderCellTailwind,
35
+ TableCellTailwind,
36
+ } from "./TableV2Tailwind";
37
+
38
+ type HybridProps = {
39
+ className?: string;
40
+ children?: React.ReactNode;
41
+ [key: string]: any;
42
+ };
43
+
44
+ const TableContainerContext = React.createContext<string | null>(null);
45
+
46
+ export const useTableContainerId = () =>
47
+ React.useContext(TableContainerContext);
48
+
49
+ export const TableWrapper = React.forwardRef<
50
+ HTMLDivElement,
51
+ { children: React.ReactElement } & HybridProps
52
+ >(({ children, className, ...rest }, ref) => {
53
+ const containerId = useId();
54
+
55
+ // Region semantics stay identical on both paths.
56
+ const containerProps = {
57
+ role: "region",
58
+ tabIndex: 0,
59
+ id: containerId,
60
+ "aria-labelledby": containerId,
61
+ };
62
+
63
+ return (
64
+ <TableContainerContext.Provider value={containerId}>
65
+ {needsStyledComponents({ ...rest, className }) ? (
66
+ <StyledTableV2Container
67
+ className={className}
68
+ ref={ref}
69
+ {...containerProps}
70
+ {...rest}
71
+ >
72
+ {children}
73
+ </StyledTableV2Container>
74
+ ) : (
75
+ <TableV2ContainerTailwind
76
+ className={className}
77
+ ref={ref}
78
+ {...containerProps}
79
+ {...rest}
80
+ >
81
+ {children}
82
+ </TableV2ContainerTailwind>
83
+ )}
84
+ </TableContainerContext.Provider>
85
+ );
86
+ });
87
+
88
+ TableWrapper.displayName = "TableV2.TableWrapper";
89
+
90
+ export const TableRoot = React.forwardRef<HTMLTableElement, HybridProps>(
91
+ ({ className, children, ...rest }, ref) => {
92
+ if (needsStyledComponents({ ...rest, className })) {
93
+ return (
94
+ <StyledTableV2 className={className} ref={ref} {...rest}>
95
+ {children}
96
+ </StyledTableV2>
97
+ );
98
+ }
99
+ return (
100
+ <TableRootTailwind className={className} ref={ref} {...rest}>
101
+ {children}
102
+ </TableRootTailwind>
103
+ );
104
+ }
105
+ );
106
+
107
+ TableRoot.displayName = "TableV2.Table";
108
+
109
+ export const TableCaption = React.forwardRef<
110
+ HTMLTableCaptionElement,
111
+ HybridProps & { isVisible?: boolean }
112
+ >(({ className, children, isVisible = true, ...rest }, ref) => {
113
+ if (needsStyledComponents({ ...rest, className })) {
114
+ return (
115
+ <StyledCaption
116
+ className={className}
117
+ isVisible={isVisible}
118
+ ref={ref}
119
+ {...rest}
120
+ >
121
+ {children}
122
+ </StyledCaption>
123
+ );
124
+ }
125
+ return (
126
+ <CaptionTailwind
127
+ className={className}
128
+ isVisible={isVisible}
129
+ ref={ref}
130
+ {...rest}
131
+ >
132
+ {children}
133
+ </CaptionTailwind>
134
+ );
135
+ });
136
+
137
+ TableCaption.displayName = "TableV2.Caption";
138
+
139
+ export const TableHeader = React.forwardRef<
140
+ HTMLTableSectionElement,
141
+ HybridProps
142
+ >(({ className, children, ...rest }, ref) => {
143
+ if (needsStyledComponents({ ...rest, className })) {
144
+ return (
145
+ <StyledTableHeader className={className} ref={ref} {...rest}>
146
+ {children}
147
+ </StyledTableHeader>
148
+ );
149
+ }
150
+ return (
151
+ <TableHeaderTailwind className={className} ref={ref} {...rest}>
152
+ {children}
153
+ </TableHeaderTailwind>
154
+ );
155
+ });
156
+
157
+ TableHeader.displayName = "TableV2.TableHeader";
158
+
159
+ export const TableBody = React.forwardRef<HTMLTableSectionElement, HybridProps>(
160
+ ({ className, children, ...rest }, ref) => {
161
+ if (needsStyledComponents({ ...rest, className })) {
162
+ return (
163
+ <StyledTableBody className={className} ref={ref} {...rest}>
164
+ {children}
165
+ </StyledTableBody>
166
+ );
167
+ }
168
+ return (
169
+ <TableBodyTailwind className={className} ref={ref} {...rest}>
170
+ {children}
171
+ </TableBodyTailwind>
172
+ );
173
+ }
174
+ );
175
+
176
+ TableBody.displayName = "TableV2.TableBody";
177
+
178
+ export const TableRow = React.forwardRef<HTMLTableRowElement, HybridProps>(
179
+ ({ className, children, ...rest }, ref) => {
180
+ if (needsStyledComponents({ ...rest, className })) {
181
+ return (
182
+ <StyledTableRow className={className} ref={ref} {...rest}>
183
+ {children}
184
+ </StyledTableRow>
185
+ );
186
+ }
187
+ return (
188
+ <TableRowTailwind className={className} ref={ref} {...rest}>
189
+ {children}
190
+ </TableRowTailwind>
191
+ );
192
+ }
193
+ );
194
+
195
+ TableRow.displayName = "TableV2.TableRow";
196
+
197
+ export const TableHeaderCell = React.forwardRef<
198
+ HTMLTableCellElement,
199
+ HybridProps
200
+ >(({ className, children, ...rest }, ref) => {
201
+ if (needsStyledComponents({ ...rest, className })) {
202
+ return (
203
+ <StyledTableHeaderCell className={className} ref={ref} {...rest}>
204
+ {children}
205
+ </StyledTableHeaderCell>
206
+ );
207
+ }
208
+ return (
209
+ <TableHeaderCellTailwind className={className} ref={ref} {...rest}>
210
+ {children}
211
+ </TableHeaderCellTailwind>
212
+ );
213
+ });
214
+
215
+ TableHeaderCell.displayName = "TableV2.TableHeaderCell";
216
+
217
+ export const TableCell = React.forwardRef<HTMLTableCellElement, HybridProps>(
218
+ ({ className, children, ...rest }, ref) => {
219
+ if (needsStyledComponents({ ...rest, className })) {
220
+ return (
221
+ <StyledTableCell className={className} ref={ref} {...rest}>
222
+ {children}
223
+ </StyledTableCell>
224
+ );
225
+ }
226
+ return (
227
+ <TableCellTailwind className={className} ref={ref} {...rest}>
228
+ {children}
229
+ </TableCellTailwind>
230
+ );
231
+ }
232
+ );
233
+
234
+ TableCell.displayName = "TableV2.TableCell";
235
+
236
+ const TableV2 = ({
237
+ caption,
238
+ displayCaption = true,
239
+ columns,
240
+ data,
241
+ }: TableV2Props) => {
242
+ const containerId = useTableContainerId();
243
+
244
+ return (
245
+ <TableWrapper>
246
+ <TableRoot>
247
+ <TableCaption id={containerId ?? undefined} isVisible={displayCaption}>
248
+ {caption}
249
+ </TableCaption>
250
+ <TableHeader>
251
+ <TableRow>
252
+ {columns.map((column, index) => (
253
+ <TableHeaderCell key={index} scope="col">
254
+ {column}
255
+ </TableHeaderCell>
256
+ ))}
257
+ </TableRow>
258
+ </TableHeader>
259
+ <TableBody>
260
+ {data.map((row, rowIndex) => (
261
+ <TableRow key={rowIndex}>
262
+ {row.map((cell, cellIndex) => (
263
+ <TableCell key={cellIndex}>{cell}</TableCell>
264
+ ))}
265
+ </TableRow>
266
+ ))}
267
+ </TableBody>
268
+ </TableRoot>
269
+ </TableWrapper>
270
+ );
271
+ };
272
+
273
+ // Export the full Table component as Table (main export)
274
+ export const Table = TableV2;
275
+
276
+ // Default export
277
+ export default TableV2;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Tailwind CSS implementation of the v2 Table sub-components.
3
+ * Uses Seeds table CSS classes from table-v2.css.
4
+ *
5
+ * These are the pure semantic-HTML + CSS-class leaves the hybrid wrappers render
6
+ * on the fast path. They forward `className` and `ref` so `styled(Component)`
7
+ * extensions keep working, and pass standard HTML attributes (scope, id, role,
8
+ * tabIndex, …) straight through to the DOM. Every leaf honors the polymorphic
9
+ * `as` prop (rendering a dynamic element instead of a hard-coded tag) so
10
+ * `<TableCell as="th" scope="row">` produces a real `<th scope="row">` with the
11
+ * `rowheader` role and `<TableRow as="div">` renders a `<div>`, matching the
12
+ * styled-components path. Because `as` is destructured out, it never leaks onto
13
+ * the DOM node; a component-valued `as` (e.g. `styled(TableRow)`) re-enters the
14
+ * hybrid, which then detects the styled signal and routes to styled-components.
15
+ */
16
+
17
+ import React from "react";
18
+
19
+ // Utility for merging class names properly
20
+ function cn(
21
+ ...inputs: (string | undefined | null | false | Record<string, boolean>)[]
22
+ ): string {
23
+ const classes: string[] = [];
24
+
25
+ for (const input of inputs) {
26
+ if (!input) continue;
27
+
28
+ if (typeof input === "string") {
29
+ classes.push(input);
30
+ } else if (typeof input === "object") {
31
+ for (const [key, value] of Object.entries(input)) {
32
+ if (value) {
33
+ classes.push(key);
34
+ }
35
+ }
36
+ }
37
+ }
38
+
39
+ return classes.join(" ");
40
+ }
41
+
42
+ type TailwindProps = {
43
+ className?: string;
44
+ children?: React.ReactNode;
45
+ [key: string]: any;
46
+ };
47
+
48
+ export const TableV2ContainerTailwind = React.forwardRef<
49
+ HTMLDivElement,
50
+ TailwindProps
51
+ >(({ className, children, as: Component = "div", ...rest }, ref) => (
52
+ <Component
53
+ ref={ref}
54
+ className={cn("seeds-table-v2-container", className)}
55
+ {...rest}
56
+ >
57
+ {children}
58
+ </Component>
59
+ ));
60
+
61
+ TableV2ContainerTailwind.displayName = "TableV2.ContainerTailwind";
62
+
63
+ export const TableRootTailwind = React.forwardRef<
64
+ HTMLTableElement,
65
+ TailwindProps
66
+ >(({ className, children, as: Component = "table", ...rest }, ref) => (
67
+ <Component ref={ref} className={cn("seeds-table-v2", className)} {...rest}>
68
+ {children}
69
+ </Component>
70
+ ));
71
+
72
+ TableRootTailwind.displayName = "TableV2.TableTailwind";
73
+
74
+ export const CaptionTailwind = React.forwardRef<
75
+ HTMLTableCaptionElement,
76
+ TailwindProps & { isVisible?: boolean }
77
+ >(
78
+ (
79
+ {
80
+ className,
81
+ children,
82
+ isVisible = true,
83
+ as: Component = "caption",
84
+ ...rest
85
+ },
86
+ ref
87
+ ) => (
88
+ <Component
89
+ ref={ref}
90
+ className={cn(
91
+ "seeds-table-v2-caption",
92
+ { "seeds-table-v2-caption-hidden": !isVisible },
93
+ className
94
+ )}
95
+ {...rest}
96
+ >
97
+ {children}
98
+ </Component>
99
+ )
100
+ );
101
+
102
+ CaptionTailwind.displayName = "TableV2.CaptionTailwind";
103
+
104
+ export const TableHeaderTailwind = React.forwardRef<
105
+ HTMLTableSectionElement,
106
+ TailwindProps
107
+ >(({ className, children, as: Component = "thead", ...rest }, ref) => (
108
+ <Component
109
+ ref={ref}
110
+ className={cn("seeds-table-v2-header", className)}
111
+ {...rest}
112
+ >
113
+ {children}
114
+ </Component>
115
+ ));
116
+
117
+ TableHeaderTailwind.displayName = "TableV2.TableHeaderTailwind";
118
+
119
+ export const TableBodyTailwind = React.forwardRef<
120
+ HTMLTableSectionElement,
121
+ TailwindProps
122
+ >(({ className, children, as: Component = "tbody", ...rest }, ref) => (
123
+ <Component
124
+ ref={ref}
125
+ className={cn("seeds-table-v2-body", className)}
126
+ {...rest}
127
+ >
128
+ {children}
129
+ </Component>
130
+ ));
131
+
132
+ TableBodyTailwind.displayName = "TableV2.TableBodyTailwind";
133
+
134
+ export const TableRowTailwind = React.forwardRef<
135
+ HTMLTableRowElement,
136
+ TailwindProps
137
+ >(({ className, children, as: Component = "tr", ...rest }, ref) => (
138
+ <Component
139
+ ref={ref}
140
+ className={cn("seeds-table-v2-row", className)}
141
+ {...rest}
142
+ >
143
+ {children}
144
+ </Component>
145
+ ));
146
+
147
+ TableRowTailwind.displayName = "TableV2.TableRowTailwind";
148
+
149
+ export const TableHeaderCellTailwind = React.forwardRef<
150
+ HTMLTableCellElement,
151
+ TailwindProps
152
+ >(({ className, children, as: Component = "th", ...rest }, ref) => (
153
+ <Component
154
+ ref={ref}
155
+ className={cn("seeds-table-v2-header-cell", className)}
156
+ {...rest}
157
+ >
158
+ {children}
159
+ </Component>
160
+ ));
161
+
162
+ TableHeaderCellTailwind.displayName = "TableV2.TableHeaderCellTailwind";
163
+
164
+ export const TableCellTailwind = React.forwardRef<
165
+ HTMLTableCellElement,
166
+ TailwindProps
167
+ >(({ className, children, as: Component = "td", ...rest }, ref) => (
168
+ <Component
169
+ ref={ref}
170
+ className={cn("seeds-table-v2-cell", className)}
171
+ {...rest}
172
+ >
173
+ {children}
174
+ </Component>
175
+ ));
176
+
177
+ TableCellTailwind.displayName = "TableV2.TableCellTailwind";
@@ -1,5 +1,7 @@
1
1
  import React from "react";
2
+ import styled from "styled-components";
2
3
  import { render, screen } from "@sproutsocial/seeds-react-testing-library";
4
+ import { theme } from "@sproutsocial/seeds-react-theme";
3
5
  import TableV2, {
4
6
  TableWrapper,
5
7
  Table,
@@ -371,4 +373,235 @@ describe("TableV2", () => {
371
373
  expect(rows).toHaveLength(101);
372
374
  });
373
375
  });
376
+
377
+ describe("Hybrid routing", () => {
378
+ it("renders the Tailwind (CSS-class) path by default", () => {
379
+ render(
380
+ <TableV2 caption="Default" columns={mockColumns} data={mockData} />
381
+ );
382
+
383
+ expect(screen.getByRole("table")).toHaveClass("seeds-table-v2");
384
+ expect(screen.getAllByRole("cell")[0]).toHaveClass("seeds-table-v2-cell");
385
+ expect(screen.getAllByRole("columnheader")[0]).toHaveClass(
386
+ "seeds-table-v2-header-cell"
387
+ );
388
+ });
389
+
390
+ it("routes to the styled-components path when a system prop is passed", () => {
391
+ render(
392
+ <TableWrapper>
393
+ <TableRoot>
394
+ <TableBody>
395
+ <TableRow>
396
+ <TableCell px={400}>Styled Cell</TableCell>
397
+ </TableRow>
398
+ </TableBody>
399
+ </TableRoot>
400
+ </TableWrapper>
401
+ );
402
+
403
+ const cell = screen.getByText("Styled Cell");
404
+ // The styled-components fallback does not carry the Tailwind class...
405
+ expect(cell).not.toHaveClass("seeds-table-v2-cell");
406
+ // ...and the system prop must not leak onto the DOM node.
407
+ expect(cell).not.toHaveAttribute("px");
408
+ });
409
+
410
+ it("routes to the styled-components path for a styled() extension carrying a theme prop", () => {
411
+ render(
412
+ <TableWrapper>
413
+ <TableRoot>
414
+ <TableBody>
415
+ <TableRow>
416
+ <TableCell theme={theme}>Themed Cell</TableCell>
417
+ </TableRow>
418
+ </TableBody>
419
+ </TableRoot>
420
+ </TableWrapper>
421
+ );
422
+
423
+ const cell = screen.getByText("Themed Cell");
424
+ // A styled() extension injects a theme prop; it must route to the
425
+ // styled-components path, not the Tailwind leaf...
426
+ expect(cell).not.toHaveClass("seeds-table-v2-cell");
427
+ // ...and the theme object must not leak onto the DOM node.
428
+ expect(cell).not.toHaveAttribute("theme");
429
+ });
430
+
431
+ it("routes to the styled-components path for a styled() extension with an sc- className", () => {
432
+ render(
433
+ <TableWrapper>
434
+ <TableRoot>
435
+ <TableBody>
436
+ <TableRow>
437
+ <TableCell className="sc-abc123">Sc Cell</TableCell>
438
+ </TableRow>
439
+ </TableBody>
440
+ </TableRoot>
441
+ </TableWrapper>
442
+ );
443
+
444
+ const cell = screen.getByText("Sc Cell");
445
+ // The generated sc-XXXX class is the styled() signal even without a
446
+ // theme prop, so this must not land on the Tailwind leaf.
447
+ expect(cell).not.toHaveClass("seeds-table-v2-cell");
448
+ });
449
+
450
+ it("visually hides the caption when isVisible is false but keeps it in the DOM", () => {
451
+ render(
452
+ <TableWrapper>
453
+ <TableRoot>
454
+ <TableCaption isVisible={false}>Hidden Caption</TableCaption>
455
+ </TableRoot>
456
+ </TableWrapper>
457
+ );
458
+
459
+ const caption = screen.getByText("Hidden Caption");
460
+ expect(caption).toBeInTheDocument();
461
+ expect(caption).toHaveClass("seeds-table-v2-caption-hidden");
462
+ });
463
+
464
+ it("keeps the caption visible when isVisible is true", () => {
465
+ render(
466
+ <TableWrapper>
467
+ <TableRoot>
468
+ <TableCaption isVisible={true}>Visible Caption</TableCaption>
469
+ </TableRoot>
470
+ </TableWrapper>
471
+ );
472
+
473
+ const caption = screen.getByText("Visible Caption");
474
+ expect(caption).toBeVisible();
475
+ expect(caption).not.toHaveClass("seeds-table-v2-caption-hidden");
476
+ });
477
+ });
478
+
479
+ describe("Polymorphic `as` prop", () => {
480
+ it("renders TableCell as a th with the rowheader role when as='th' scope='row'", () => {
481
+ render(
482
+ <TableWrapper>
483
+ <TableRoot>
484
+ <TableBody>
485
+ <TableRow>
486
+ <TableCell as="th" scope="row">
487
+ Row Header
488
+ </TableCell>
489
+ </TableRow>
490
+ </TableBody>
491
+ </TableRoot>
492
+ </TableWrapper>
493
+ );
494
+
495
+ const cell = screen.getByRole("rowheader", { name: "Row Header" });
496
+ expect(cell.tagName).toBe("TH");
497
+ expect(cell).toHaveAttribute("scope", "row");
498
+ expect(cell).toHaveClass("seeds-table-v2-cell");
499
+ // `as` is destructured out and must not leak onto the DOM node.
500
+ expect(cell).not.toHaveAttribute("as");
501
+ });
502
+
503
+ it("renders TableCell as a td (role cell) by default", () => {
504
+ render(
505
+ <TableWrapper>
506
+ <TableRoot>
507
+ <TableBody>
508
+ <TableRow>
509
+ <TableCell>Body Cell</TableCell>
510
+ </TableRow>
511
+ </TableBody>
512
+ </TableRoot>
513
+ </TableWrapper>
514
+ );
515
+
516
+ const cell = screen.getByRole("cell", { name: "Body Cell" });
517
+ expect(cell.tagName).toBe("TD");
518
+ });
519
+
520
+ it("renders TableHeaderCell as a th by default", () => {
521
+ render(
522
+ <TableWrapper>
523
+ <TableRoot>
524
+ <TableHeader>
525
+ <TableRow>
526
+ <TableHeaderCell scope="col">Column Header</TableHeaderCell>
527
+ </TableRow>
528
+ </TableHeader>
529
+ </TableRoot>
530
+ </TableWrapper>
531
+ );
532
+
533
+ const header = screen.getByRole("columnheader", {
534
+ name: "Column Header",
535
+ });
536
+ expect(header.tagName).toBe("TH");
537
+ });
538
+
539
+ it("renders a non-cell leaf (TableRow) as the requested element for a string `as`", () => {
540
+ render(
541
+ <TableWrapper>
542
+ <TableRoot>
543
+ <TableBody>
544
+ <TableRow as="div">
545
+ <TableCell>Cell in a div row</TableCell>
546
+ </TableRow>
547
+ </TableBody>
548
+ </TableRoot>
549
+ </TableWrapper>
550
+ );
551
+
552
+ const row = screen
553
+ .getByText("Cell in a div row")
554
+ .closest(".seeds-table-v2-row");
555
+ expect(row).not.toBeNull();
556
+ // The requested element is rendered while the Tailwind class is kept...
557
+ expect(row?.tagName).toBe("DIV");
558
+ // ...and `as` is destructured out, so it must not leak onto the DOM node.
559
+ expect(row).not.toHaveAttribute("as");
560
+ });
561
+
562
+ it("renders a non-cell leaf (TableBody) as the requested element for a string `as`", () => {
563
+ render(
564
+ <TableWrapper>
565
+ <TableRoot>
566
+ <TableBody as="section">
567
+ <TableRow>
568
+ <TableCell>Cell in a section body</TableCell>
569
+ </TableRow>
570
+ </TableBody>
571
+ </TableRoot>
572
+ </TableWrapper>
573
+ );
574
+
575
+ const body = screen
576
+ .getByText("Cell in a section body")
577
+ .closest(".seeds-table-v2-body");
578
+ expect(body).not.toBeNull();
579
+ expect(body?.tagName).toBe("SECTION");
580
+ expect(body).not.toHaveAttribute("as");
581
+ });
582
+
583
+ it("routes a non-cell leaf to the styled-components path for a component-valued `as`", () => {
584
+ const StyledRow = styled(TableRow)``;
585
+ render(
586
+ <TableWrapper>
587
+ <TableRoot>
588
+ <TableBody>
589
+ <TableRow as={StyledRow}>
590
+ <TableCell>Cell in a styled row</TableCell>
591
+ </TableRow>
592
+ </TableBody>
593
+ </TableRoot>
594
+ </TableWrapper>
595
+ );
596
+
597
+ const row = screen.getByText("Cell in a styled row").closest("tr");
598
+ // A component-valued `as` (the documented `tableRowProps={{ as: styled(...) }}`
599
+ // pattern) re-enters the hybrid, which detects the injected styled signal and
600
+ // renders a real <tr> via the styled-components path...
601
+ expect(row).not.toBeNull();
602
+ // ...and the component reference must never leak onto the DOM as an `as`
603
+ // attribute, which was the reported regression.
604
+ expect(row).not.toHaveAttribute("as");
605
+ });
606
+ });
374
607
  });