From ce9e0e1ff1717544ebfec1459ae097796ac4b94c Mon Sep 17 00:00:00 2001 From: myu-box Date: Tue, 21 Apr 2026 16:20:22 +0200 Subject: [PATCH 1/3] feat(docgen-sidebar): support new types for pdf template --- .../DocGenSidebar/DocGenSidebar.tsx | 131 ++++++++++-------- .../DocGenSidebar/messages.tsx | 15 ++ .../stories/DocGenSidebar.stories.tsx | 31 ++++- .../content-sidebar/DocGenSidebar/types.ts | 18 ++- .../__mocks__/DocGenSidebar.mock.ts | 28 ++++ .../__tests__/DocGenSidebar.test.tsx | 21 ++- 6 files changed, 186 insertions(+), 58 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 3f35cddd7a..3a65af9d0b 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -1,7 +1,7 @@ import React, { useState, useCallback, useEffect } from 'react'; import classNames from 'classnames'; import flow from 'lodash/flow'; -import { useIntl } from 'react-intl'; +import { useIntl, type MessageDescriptor } from 'react-intl'; import { LoadingIndicator } from '@box/blueprint-web'; @@ -31,10 +31,77 @@ import { WithLoggerProps } from '../../../common/types/logging'; import commonMessages from '../../common/messages'; import './DocGenSidebar.scss'; -import { DocGenTag, DocGenTemplateTagsResponse, JsonPathsMap } from './types'; +import type { DocGenTag, DocGenTemplateTagsResponse, JsonPathsMap } from './types'; +import { PDF_FIELD_TAG_TYPES, isPdfFormFieldTagType } from './types'; const DEFAULT_RETRIES = 10; +type DocGenSection = { + id: string; + message: MessageDescriptor; + tree: JsonPathsMap; +}; + +const PDF_FIELD_TYPE_MESSAGES: Record<(typeof PDF_FIELD_TAG_TYPES)[number], MessageDescriptor> = { + checkbox: messages.checkboxTags, + radiobutton: messages.radiobuttonTags, + dropdown: messages.dropdownTags, +}; + +const createNestedObject = (base: JsonPathsMap, paths: string[]) => { + paths.reduce((obj, path) => { + if (!obj[path]) obj[path] = {}; + return obj[path]; + }, base); +}; +const tagsToJsonPaths = (docGenTags: DocGenTag[]): JsonPathsMap => { + const jsonPathsMap: JsonPathsMap = {}; + + docGenTags.forEach(tag => { + tag.json_paths.forEach(jsonPath => { + const paths = jsonPath.split('.'); + createNestedObject(jsonPathsMap, paths); + }); + }); + + return jsonPathsMap; +}; + +const buildDocGenSections = (data: DocGenTag[]): DocGenSection[] => { + const result: DocGenSection[] = []; + const imageTags = data.filter(tag => tag.tag_type === 'image'); + const textTags = data.filter(tag => tag.tag_type !== 'image' && !isPdfFormFieldTagType(tag.tag_type)); + + if (textTags.length > 0) { + result.push({ + id: 'text', + message: messages.textTags, + tree: tagsToJsonPaths(textTags), + }); + } + + if (imageTags.length > 0) { + result.push({ + id: 'image', + message: messages.imageTags, + tree: tagsToJsonPaths(imageTags), + }); + } + + PDF_FIELD_TAG_TYPES.forEach(fieldType => { + const fieldTags = data.filter(tag => tag.tag_type === fieldType); + if (fieldTags.length > 0) { + result.push({ + id: fieldType, + message: PDF_FIELD_TYPE_MESSAGES[fieldType], + tree: tagsToJsonPaths(fieldTags), + }); + } + }); + + return result; +}; + type ExternalProps = { enabled: boolean; getDocGenTags: () => Promise; @@ -44,49 +111,12 @@ type ExternalProps = { type Props = ExternalProps & ErrorContextProps & WithLoggerProps; -type TagState = { - text: DocGenTag[]; - image: DocGenTag[]; -}; - -type JsonPathsState = { - textTree: JsonPathsMap; - imageTree: JsonPathsMap; -}; - const DocGenSidebar = ({ getDocGenTags }: Props) => { const { formatMessage } = useIntl(); const [hasError, setHasError] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [tags, setTags] = useState({ - text: [], - image: [], - }); - const [jsonPaths, setJsonPaths] = useState({ - textTree: {}, - imageTree: {}, - }); - - const createNestedObject = (base: JsonPathsMap, paths: string[]) => { - paths.reduce((obj, path) => { - if (!obj[path]) obj[path] = {}; - return obj[path]; - }, base); - }; - - const tagsToJsonPaths = useCallback((docGenTags: DocGenTag[]): JsonPathsMap => { - const jsonPathsMap: JsonPathsMap = {}; - - docGenTags.forEach(tag => { - tag.json_paths.forEach(jsonPath => { - const paths = jsonPath.split('.'); - createNestedObject(jsonPathsMap, paths); - }); - }); - - return jsonPathsMap; - }, []); + const [sections, setSections] = useState([]); const loadTags = useCallback( async (attempts = DEFAULT_RETRIES) => { @@ -101,17 +131,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { loadTags.call(this, attempts - 1); } else if (response?.data) { const { data } = response; - // anything that is not an image tag for this view is treated as a text tag - const textTags = data?.filter(tag => tag.tag_type !== 'image') || []; - const imageTags = data?.filter(tag => tag.tag_type === 'image') || []; - setTags({ - text: textTags, - image: imageTags, - }); - setJsonPaths({ - textTree: tagsToJsonPaths(textTags), - imageTree: tagsToJsonPaths(imageTags), - }); + setSections(buildDocGenSections(data)); setHasError(false); setIsLoading(false); } else { @@ -125,14 +145,14 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { }, // disabling eslint because the getDocGenTags prop is changing very frequently // eslint-disable-next-line react-hooks/exhaustive-deps - [tagsToJsonPaths], + [], ); useEffect(() => { loadTags(DEFAULT_RETRIES); }, [loadTags]); - const isEmpty = tags.image.length + tags.text.length === 0; + const isEmpty = sections.length === 0; return ( @@ -147,8 +167,9 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { {!hasError && !isLoading && isEmpty && } {!hasError && !isLoading && !isEmpty && ( <> - - + {sections.map(section => ( + + ))} )} diff --git a/src/elements/content-sidebar/DocGenSidebar/messages.tsx b/src/elements/content-sidebar/DocGenSidebar/messages.tsx index 7c43f97a4e..65b9c5c961 100644 --- a/src/elements/content-sidebar/DocGenSidebar/messages.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/messages.tsx @@ -11,6 +11,21 @@ const messages = defineMessages({ description: 'Image tags section header', defaultMessage: 'Image tags', }, + checkboxTags: { + id: 'be.docGenSidebar.checkboxTags', + description: 'Checkbox tags section header', + defaultMessage: 'Checkbox tags', + }, + radiobuttonTags: { + id: 'be.docGenSidebar.radiobuttonTags', + description: 'Radiobutton tags section header', + defaultMessage: 'Radiobutton tags', + }, + dropdownTags: { + id: 'be.docGenSidebar.dropdownTags', + description: 'Dropdown tags section header', + defaultMessage: 'Dropdown tags', + }, docGenTags: { id: 'be.docGenSidebar.docGenTags', description: 'DocGen sidebar header', diff --git a/src/elements/content-sidebar/DocGenSidebar/stories/DocGenSidebar.stories.tsx b/src/elements/content-sidebar/DocGenSidebar/stories/DocGenSidebar.stories.tsx index 289d33f621..5043d1a88d 100644 --- a/src/elements/content-sidebar/DocGenSidebar/stories/DocGenSidebar.stories.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/stories/DocGenSidebar.stories.tsx @@ -4,7 +4,7 @@ import type { HttpHandler } from 'msw'; import type { Meta } from '@storybook/react'; import ContentSidebar from '../../ContentSidebar'; import { mockFileRequest } from '../../stories/__mocks__/ContentSidebarMocks'; -import mockDocGenTags from '../../__mocks__/DocGenSidebar.mock'; +import mockDocGenTags, { mockPdfTemplateData } from '../../__mocks__/DocGenSidebar.mock'; const defaultArgs = { detailsSidebarProps: { @@ -33,6 +33,16 @@ const docGenSidebarProps = { }), }; +const docGenSidebarPdfTemplateProps = { + enabled: true, + isDocGenTemplate: true, + checkDocGenTemplate: noop, + getDocGenTags: async () => ({ + pagination: {}, + data: mockPdfTemplateData, + }), +}; + export const basic = { args: { defaultView: 'docgen', @@ -52,6 +62,25 @@ export const withModernizedBlueprint = { }, }; +export const pdfTemplate = { + args: { + defaultView: 'docgen', + docGenSidebarProps: docGenSidebarPdfTemplateProps, + }, +}; + +export const pdfTemplateWithModernizedBlueprint = { + args: { + enableModernizedComponents: true, + defaultView: 'docgen', + docGenSidebarProps: docGenSidebarPdfTemplateProps, + features: { + ...global.FEATURE_FLAGS, + previewModernization: { enabled: true }, + }, + }, +}; + const meta: Meta & { parameters: { msw: { handlers: HttpHandler[] } } } = { title: 'Elements/ContentSidebar/DocGenSidebar', component: ContentSidebar, diff --git a/src/elements/content-sidebar/DocGenSidebar/types.ts b/src/elements/content-sidebar/DocGenSidebar/types.ts index 2730d827e0..88aebc4d27 100644 --- a/src/elements/content-sidebar/DocGenSidebar/types.ts +++ b/src/elements/content-sidebar/DocGenSidebar/types.ts @@ -1,7 +1,16 @@ // our apis are in snake case export type DocGenTag = { /* eslint-disable-next-line camelcase */ - tag_type: 'text' | 'arithmetic' | 'conditional' | 'for-loop' | 'table-loop' | 'image'; + tag_type: + | 'text' + | 'arithmetic' + | 'conditional' + | 'for-loop' + | 'table-loop' + | 'image' + | 'checkbox' + | 'radiobutton' + | 'dropdown'; /* eslint-disable-next-line camelcase */ tag_content: string; /* eslint-disable-next-line camelcase */ @@ -20,3 +29,10 @@ export type DocGenTemplateTagsResponse = { export interface JsonPathsMap { [key: string]: JsonPathsMap | {}; } + +/** PDF template control tags that render in their own sidebar section. */ +export const PDF_FIELD_TAG_TYPES = ['checkbox', 'radiobutton', 'dropdown'] as const; + +export function isPdfFormFieldTagType(tagType: DocGenTag['tag_type']): boolean { + return (PDF_FIELD_TAG_TYPES as readonly DocGenTag['tag_type'][]).includes(tagType); +} diff --git a/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts b/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts index 97edb53e59..66bbae7117 100644 --- a/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts +++ b/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts @@ -113,4 +113,32 @@ const mockData = [ }, ]; +/** PDF template tags (text, checkbox, radiobutton, dropdown) */ +export const mockPdfTemplateData = [ + { + tag_type: 'text', + tag_content: '{{NameField::optional}}', + json_paths: ['NameField'], + required: false, + }, + { + tag_type: 'checkbox', + tag_content: '{{SubscribeCheckbox}}', + json_paths: ['SubscribeCheckbox'], + required: true, + }, + { + tag_type: 'radiobutton', + tag_content: '{{Gender}}', + json_paths: ['Gender'], + required: true, + }, + { + tag_type: 'dropdown', + tag_content: '{{CountryDropdown}}', + json_paths: ['CountryDropdown'], + required: true, + }, +]; + export default mockData; diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 8539a66f68..4a3069a626 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render, screen, waitFor, fireEvent } from '../../../test-utils/testing-library'; import { DocGenSidebarComponent as DocGenSidebar } from '../DocGenSidebar/DocGenSidebar'; -import mockData from '../__mocks__/DocGenSidebar.mock'; +import mockData, { mockPdfTemplateData } from '../__mocks__/DocGenSidebar.mock'; const docGenSidebarProps = { getDocGenTags: jest.fn().mockReturnValue( @@ -60,6 +60,25 @@ describe('elements/content-sidebar/DocGenSidebar', () => { expect(tagList).toHaveLength(2); }); + test('should render PDF template tags in separate sections', async () => { + renderComponent({ + getDocGenTags: jest.fn().mockReturnValue( + Promise.resolve({ + pagination: {}, + data: mockPdfTemplateData, + }), + ), + }); + + const tagList = await screen.findAllByTestId('bcs-TagsSection'); + expect(tagList).toHaveLength(4); + + expect(await screen.findByText('Text tags')).toBeInTheDocument(); + expect(screen.getByText('Checkbox tags')).toBeInTheDocument(); + expect(screen.getByText('Radiobutton tags')).toBeInTheDocument(); + expect(screen.getByText('Dropdown tags')).toBeInTheDocument(); + }); + test('should render DocGen sidebar component correctly with tags list', async () => { renderComponent(); const parentTag = await screen.findByText('about'); From e846aeee98313bdcdd7480cdff5f079523cbf5d5 Mon Sep 17 00:00:00 2001 From: myu-box Date: Wed, 22 Apr 2026 23:14:41 +0200 Subject: [PATCH 2/3] feat: update DocGenDoc type and tests --- .../DocGenSidebar/DocGenSidebar.tsx | 26 +++++----- .../content-sidebar/DocGenSidebar/types.ts | 1 + .../__mocks__/DocGenSidebar.mock.ts | 22 +++++++++ .../__tests__/DocGenSidebar.test.tsx | 48 ++++++++++++------- 4 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 3a65af9d0b..f93f019bc3 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -48,35 +48,33 @@ const PDF_FIELD_TYPE_MESSAGES: Record<(typeof PDF_FIELD_TAG_TYPES)[number], Mess dropdown: messages.dropdownTags, }; -const createNestedObject = (base: JsonPathsMap, paths: string[]) => { - paths.reduce((obj, path) => { - if (!obj[path]) obj[path] = {}; - return obj[path]; - }, base); -}; -const tagsToJsonPaths = (docGenTags: DocGenTag[]): JsonPathsMap => { - const jsonPathsMap: JsonPathsMap = {}; +const buildJsonPathTree = (docGenTags: DocGenTag[]): JsonPathsMap => { + const pathTree: JsonPathsMap = {}; docGenTags.forEach(tag => { tag.json_paths.forEach(jsonPath => { - const paths = jsonPath.split('.'); - createNestedObject(jsonPathsMap, paths); + const segments = jsonPath.split('.'); + segments.reduce((node, segment) => { + if (!node[segment]) node[segment] = {}; + return node[segment]; + }, pathTree); }); }); - return jsonPathsMap; + return pathTree; }; const buildDocGenSections = (data: DocGenTag[]): DocGenSection[] => { const result: DocGenSection[] = []; const imageTags = data.filter(tag => tag.tag_type === 'image'); + // anything that is not an image tag or pdf tag would be treated as a text tag const textTags = data.filter(tag => tag.tag_type !== 'image' && !isPdfFormFieldTagType(tag.tag_type)); if (textTags.length > 0) { result.push({ id: 'text', message: messages.textTags, - tree: tagsToJsonPaths(textTags), + tree: buildJsonPathTree(textTags), }); } @@ -84,7 +82,7 @@ const buildDocGenSections = (data: DocGenTag[]): DocGenSection[] => { result.push({ id: 'image', message: messages.imageTags, - tree: tagsToJsonPaths(imageTags), + tree: buildJsonPathTree(imageTags), }); } @@ -94,7 +92,7 @@ const buildDocGenSections = (data: DocGenTag[]): DocGenSection[] => { result.push({ id: fieldType, message: PDF_FIELD_TYPE_MESSAGES[fieldType], - tree: tagsToJsonPaths(fieldTags), + tree: buildJsonPathTree(fieldTags), }); } }); diff --git a/src/elements/content-sidebar/DocGenSidebar/types.ts b/src/elements/content-sidebar/DocGenSidebar/types.ts index 88aebc4d27..60c6c5f7ce 100644 --- a/src/elements/content-sidebar/DocGenSidebar/types.ts +++ b/src/elements/content-sidebar/DocGenSidebar/types.ts @@ -15,6 +15,7 @@ export type DocGenTag = { tag_content: string; /* eslint-disable-next-line camelcase */ json_paths: Array; + required: boolean; }; export type DocGenTemplateTagsResponse = { diff --git a/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts b/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts index 66bbae7117..d13d5478df 100644 --- a/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts +++ b/src/elements/content-sidebar/__mocks__/DocGenSidebar.mock.ts @@ -3,113 +3,135 @@ const mockData = [ tag_content: '{{ isActive }}', tag_type: 'text', json_paths: ['isActive'], + required: true, }, { tag_content: '{{ about }}', tag_type: 'text', json_paths: ['about', 'about.name'], + required: true, }, { tag_content: '{{ phone }}', tag_type: 'text', json_paths: ['phone'], + required: true, }, { tag_content: '{{ company }}', tag_type: 'text', json_paths: ['company', 'company.name'], + required: true, }, { tag_content: '{{contract.customerName}}', tag_type: 'text', json_paths: ['contract', 'contract.customerName'], + required: true, }, { tag_content: '{{contract.customerAddress.street}}', tag_type: 'text', json_paths: ['contract', 'contract.customerAddress', 'contract.customerAddress.street'], + required: true, }, { tag_content: '{{contract.customerAddress.city}}', tag_type: 'text', json_paths: ['contract', 'contract.customerAddress', 'contract.customerAddress.city'], + required: true, }, { tag_content: '{{if contract.country == “UK”}}', tag_type: 'conditional', json_paths: ['contract', 'contract.country'], + required: true, }, { tag_content: '{{if contract.country == “1111” and contract.city == “London” }}', tag_type: 'conditional', json_paths: ['contract', 'contract.country', 'contract.city'], + required: true, }, { tag_content: '{{elseif contract.country == “JAPAN” and contract.city == “Tokyo“}}', tag_type: 'conditional', json_paths: ['contract', 'contract.country', 'contract.city'], + required: true, }, { tag_content: '{{invoice.image}}', tag_type: 'image', json_paths: ['invoice', 'invoice.image'], + required: true, }, { tag_content: '{{item.quantity * item.price}}', tag_type: 'arithmetic', json_paths: ['products', 'products.quantity', 'products.price'], + required: true, }, { tag_content: '{{tablerow item in products }}', tag_type: 'table-loop', json_paths: ['products'], + required: true, }, { tag_content: '{{item.name}}', tag_type: 'text', json_paths: ['products', 'products.name', 'products.quantity', 'products.price'], + required: true, }, { tag_content: '{{item.quantity * item.price}}', tag_type: 'arithmetic', json_paths: ['products', 'products.quantity', 'products.price'], + required: true, }, { tag_content: '{{$sum(products.amount)}}', tag_type: 'arithmetic', json_paths: ['products', 'products.amount'], + required: true, }, { tag_content: '{{invoice.id}}', tag_type: 'text', json_paths: ['invoice', 'invoice.id'], + required: true, }, { tag_content: '{{invoice.date}}', tag_type: 'text', json_paths: ['invoice', 'invoice.date'], + required: true, }, { tag_content: '{{invoice.billingAddress.street::uppercase}}', tag_type: 'text', json_paths: ['invoice', 'invoice.billingAddress', 'invoice.billingAddress.street'], + required: true, }, { tag_content: '{{tablerow item in products }}', tag_type: 'table-loop', json_paths: ['products', 'products.name', 'products.description', 'products.quantity', 'products.price'], + required: true, }, { tag_content: '{{item.name}}', tag_type: 'text', json_paths: ['products', 'products.name', 'products.quantity', 'products.price'], + required: true, }, { tag_content: '{{item.quantity * item.price}}', tag_type: 'arithmetic', json_paths: ['products', 'products.quantity', 'products.price'], + required: true, }, ]; diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 4a3069a626..2ca1a22e21 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { render, screen, waitFor, fireEvent } from '../../../test-utils/testing-library'; import { DocGenSidebarComponent as DocGenSidebar } from '../DocGenSidebar/DocGenSidebar'; +import type { DocGenTag } from '../DocGenSidebar/types'; import mockData, { mockPdfTemplateData } from '../__mocks__/DocGenSidebar.mock'; const docGenSidebarProps = { @@ -27,7 +28,6 @@ const processAndResolveMock = jest }), ); -const noTagsMock = jest.fn().mockReturnValue(Promise.resolve({ data: [] })); const processingTagsMock = jest.fn().mockReturnValue( Promise.resolve({ message: 'Processing tags for this file.', @@ -56,11 +56,19 @@ describe('elements/content-sidebar/DocGenSidebar', () => { test('should render DocGen sidebar component correctly with tags list', async () => { renderComponent(); - const tagList = await screen.findAllByTestId('bcs-TagsSection'); - expect(tagList).toHaveLength(2); + expect(await screen.findByText('Text tags')).toBeInTheDocument(); + expect(await screen.findByText('Image tags')).toBeInTheDocument(); + }); + + test('should show empty state when the API returns no tags', async () => { + renderComponent({ + getDocGenTags: jest.fn().mockReturnValue(Promise.resolve({ pagination: {}, data: [] })), + }); + expect(await screen.findByText('This document has no tags')).toBeInTheDocument(); + expect(screen.queryByText('Text tags')).not.toBeInTheDocument(); }); - test('should render PDF template tags in separate sections', async () => { + test('should render PDF form field tags in separate sections', async () => { renderComponent({ getDocGenTags: jest.fn().mockReturnValue( Promise.resolve({ @@ -70,13 +78,26 @@ describe('elements/content-sidebar/DocGenSidebar', () => { ), }); - const tagList = await screen.findAllByTestId('bcs-TagsSection'); - expect(tagList).toHaveLength(4); + expect(await screen.findByText('Text tags')).toBeInTheDocument(); + expect(await screen.findByText('Checkbox tags')).toBeInTheDocument(); + expect(await screen.findByText('Radiobutton tags')).toBeInTheDocument(); + expect(await screen.findByText('Dropdown tags')).toBeInTheDocument(); + }); + test('should list tags with an unknown `tag_type` under Text tags', async () => { + const data: DocGenTag[] = [ + { + tag_type: 'unknown', + tag_content: '{{x}}', + json_paths: ['pathFromUnknown'], + required: true, + } as unknown as DocGenTag, + ]; + renderComponent({ + getDocGenTags: jest.fn().mockReturnValue(Promise.resolve({ pagination: {}, data })), + }); expect(await screen.findByText('Text tags')).toBeInTheDocument(); - expect(screen.getByText('Checkbox tags')).toBeInTheDocument(); - expect(screen.getByText('Radiobutton tags')).toBeInTheDocument(); - expect(screen.getByText('Dropdown tags')).toBeInTheDocument(); + expect(await screen.findByText('pathFromUnknown')).toBeInTheDocument(); }); test('should render DocGen sidebar component correctly with tags list', async () => { @@ -93,15 +114,6 @@ describe('elements/content-sidebar/DocGenSidebar', () => { expect(nestedTag).toBeInTheDocument(); }); - test('should render empty state when there are no tags', async () => { - renderComponent({ - getDocGenTags: noTagsMock, - }); - - const emptyState = await screen.findByText('This document has no tags'); - expect(emptyState).toBeInTheDocument(); - }); - test('should render loading state', async () => { const mockGetDocGenTags = jest.fn().mockReturnValue( new Promise(resolve => { From 37f7e33e12f3dafd7eaf6e3c4d3ce07cd4ce083d Mon Sep 17 00:00:00 2001 From: myu-box Date: Fri, 24 Apr 2026 11:17:13 +0200 Subject: [PATCH 3/3] chore: trigger CI