@availity/mui-tree 0.1.1 → 0.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [0.2.0](https://github.com/Availity/element/compare/@availity/mui-tree@0.1.1...@availity/mui-tree@0.2.0) (2024-09-18)
6
+
7
+
8
+ ### Features
9
+
10
+ * **mui-tree:** add JsonViewer component ([b17a9c8](https://github.com/Availity/element/commit/b17a9c82f4348fd51da10bd26a5944f32405db0c))
11
+
5
12
  ## [0.1.1](https://github.com/Availity/element/compare/@availity/mui-tree@0.1.0...@availity/mui-tree@0.1.1) (2024-09-11)
6
13
 
7
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@availity/mui-tree",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Availity MUI Tree Component - part of the @availity/element design system",
5
5
  "keywords": [
6
6
  "react",
@@ -24,7 +24,7 @@ const items: TreeViewBaseItem[] = [
24
24
  id: 'tree-1',
25
25
  label: 'Tree 1',
26
26
  children: [
27
- { id: 'sub-tree-1.1', label: 'Sub Tree 1.1' },
27
+ { id: 'sub-tree-1.1', label: 'Sub Tree 1.1', children: [{ id: 'sub-tree-1.1.1', label: 'Sub Tree 1.1.1' }] },
28
28
  { id: 'sub-tree-1.2', label: 'Sub Tree 1.2' },
29
29
  { id: 'sub-tree-1.3', label: 'Sub Tree 1.3' },
30
30
  ],
@@ -0,0 +1,19 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import JsonViewer, { JsonViewerProps } from './JsonViewer';
4
+ // import README from '../README.md';
5
+
6
+ const meta: Meta<typeof JsonViewer> = {
7
+ title: 'Components/TreeView/JsonViewer',
8
+ component: JsonViewer,
9
+ tags: ['autodocs'],
10
+ };
11
+
12
+ export default meta;
13
+
14
+ export const _JsonViewer: StoryObj<typeof JsonViewer> = {
15
+ render: ({ data, ...rest }: JsonViewerProps) => <JsonViewer data={data} {...rest} />,
16
+ args: {
17
+ data: { foo: { bar: { baz: ['stuff', 'things', 'etc.'] } } },
18
+ },
19
+ };
@@ -0,0 +1,21 @@
1
+ import { render, fireEvent } from '@testing-library/react';
2
+ import JsonViewer from './JsonViewer';
3
+
4
+ const setup = (data: Record<string, unknown>) => render(<JsonViewer data={data} />);
5
+
6
+ describe('JsonViewer', () => {
7
+ test('does not render keys with null or undefined values without throwing error', () => {
8
+ const { getByText, queryByText } = setup({ foo: 'bar', baz: null, somethingElse: undefined });
9
+ const el = getByText(/bar/i);
10
+ expect(el).toBeInTheDocument();
11
+ const ghost = queryByText('baz');
12
+ expect(ghost).not.toBeInTheDocument();
13
+ });
14
+ test('renders deeply nested objects into the dom so they can be read by screenreaders even if parent elements are not open', () => {
15
+ const { getByText } = setup({ foo: { bar: { baz: ['stuff', 'things', 'etc.'] } } });
16
+ fireEvent.click(getByText('foo: { } 1 keys'));
17
+ fireEvent.click(getByText('bar: { } 1 keys'));
18
+ fireEvent.click(getByText('baz: [ ] 3 items'));
19
+ expect(getByText('0: stuff')).toBeInTheDocument();
20
+ });
21
+ });
@@ -0,0 +1,43 @@
1
+ import { TreeItem } from './TreeItem';
2
+ import { TreeView, TreeViewProps } from './TreeView';
3
+
4
+ export interface JsonViewerProps extends Omit<TreeViewProps, 'children'> {
5
+ /** Data to be rendered, can be most valid javascript objects, some uncommon types may not by fully supported - like cyclical objects, proxies, symbols as keys. */
6
+ data: Record<string, unknown>;
7
+ }
8
+
9
+ const isPrimitive = (value: unknown): value is string | number | boolean => {
10
+ return ['string', 'number', 'boolean'].includes(typeof value);
11
+ };
12
+
13
+ const isObject = (value: unknown): value is Record<string, unknown> => {
14
+ return !!value && typeof value === 'object';
15
+ };
16
+
17
+ const getDetails = ({ data }: JsonViewerProps): (JSX.Element | null)[] => {
18
+ return Object.entries(data).map((entry) => {
19
+ const [key, value] = entry;
20
+
21
+ if (isPrimitive(value)) {
22
+ const label = `${key}: ${value.toString()}`;
23
+ return <TreeItem label={label} itemId={`${key}.${value}`} key={`${key}.${value}`} />;
24
+ }
25
+ if (isObject(value)) {
26
+ const label = `${key}: ${Array.isArray(value) ? `[ ] ${value.length} items` : `{ } ${Object.keys(value).length} keys`}`;
27
+ return (
28
+ <TreeItem label={label} itemId={key} key={key}>
29
+ {getDetails({ data: value })}
30
+ </TreeItem>
31
+ );
32
+ }
33
+
34
+ return null;
35
+ });
36
+ };
37
+
38
+ const JsonViewer = ({ data, ...rest }: JsonViewerProps): JSX.Element => {
39
+ const details = getDetails({ data });
40
+ return <TreeView {...rest}>{details}</TreeView>;
41
+ };
42
+
43
+ export default JsonViewer;