@redocly/theme 0.58.1 → 0.59.0-next.1

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 (104) hide show
  1. package/lib/components/Accordion/Accordion.d.ts +12 -0
  2. package/lib/components/Accordion/Accordion.js +75 -0
  3. package/lib/components/Accordion/AccordionBody.d.ts +8 -0
  4. package/lib/components/Accordion/AccordionBody.js +63 -0
  5. package/lib/components/Accordion/AccordionHeader.d.ts +10 -0
  6. package/lib/components/Accordion/AccordionHeader.js +37 -0
  7. package/lib/components/Accordion/AccordionTitle.d.ts +6 -0
  8. package/lib/components/Accordion/AccordionTitle.js +20 -0
  9. package/lib/components/Accordion/variables.d.ts +1 -0
  10. package/lib/components/Accordion/variables.js +59 -0
  11. package/lib/components/Buttons/AIAssistantButton.d.ts +2 -0
  12. package/lib/components/Buttons/AIAssistantButton.js +125 -0
  13. package/lib/components/Buttons/variables.d.ts +1 -0
  14. package/lib/components/Buttons/variables.dark.d.ts +1 -0
  15. package/lib/components/Buttons/variables.dark.js +10 -0
  16. package/lib/components/Buttons/variables.js +51 -0
  17. package/lib/components/Catalog/Catalog.js +3 -3
  18. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
  19. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
  20. package/lib/components/Catalog/CatalogFilter/CatalogFilter.d.ts +6 -0
  21. package/lib/components/Catalog/CatalogFilter/CatalogFilter.js +35 -0
  22. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.d.ts +6 -0
  23. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +142 -0
  24. package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.d.ts +13 -0
  25. package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.js +92 -0
  26. package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.d.ts +6 -0
  27. package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.js +111 -0
  28. package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.d.ts +6 -0
  29. package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.js +116 -0
  30. package/lib/components/Catalog/CatalogSelector.js +0 -1
  31. package/lib/components/Catalog/variables.js +0 -1
  32. package/lib/components/Filter/FilterInput.d.ts +1 -0
  33. package/lib/components/Filter/FilterInput.js +2 -2
  34. package/lib/components/Filter/FilterOptions.js +2 -0
  35. package/lib/components/Filter/variables.js +7 -4
  36. package/lib/components/Search/SearchAiDialog.js +2 -3
  37. package/lib/components/Search/SearchAiResponse.js +2 -3
  38. package/lib/components/Search/SearchDialog.d.ts +2 -1
  39. package/lib/components/Search/SearchDialog.js +2 -2
  40. package/lib/components/Tag/Tag.d.ts +3 -2
  41. package/lib/components/Tag/Tag.js +21 -5
  42. package/lib/components/Tag/variables.dark.js +135 -0
  43. package/lib/components/Tag/variables.js +120 -58
  44. package/lib/core/hooks/use-tabs.d.ts +11 -6
  45. package/lib/core/hooks/use-tabs.js +117 -207
  46. package/lib/core/styles/dark.js +29 -26
  47. package/lib/core/styles/global.js +64 -59
  48. package/lib/core/types/l10n.d.ts +1 -1
  49. package/lib/core/utils/index.d.ts +1 -0
  50. package/lib/core/utils/index.js +1 -0
  51. package/lib/core/utils/tabs.d.ts +1 -0
  52. package/lib/core/utils/tabs.js +8 -0
  53. package/lib/icons/RedoclyIcon/RedoclyIcon.d.ts +9 -0
  54. package/lib/icons/RedoclyIcon/RedoclyIcon.js +27 -0
  55. package/lib/index.d.ts +2 -0
  56. package/lib/index.js +2 -0
  57. package/lib/layouts/RootLayout.js +6 -1
  58. package/lib/markdoc/components/Tabs/Tab.js +1 -1
  59. package/lib/markdoc/components/Tabs/TabList.d.ts +2 -14
  60. package/lib/markdoc/components/Tabs/TabList.js +63 -16
  61. package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -2
  62. package/lib/markdoc/components/Tabs/Tabs.js +11 -87
  63. package/lib/markdoc/tags/tabs.js +5 -0
  64. package/package.json +2 -2
  65. package/src/components/Accordion/Accordion.tsx +100 -0
  66. package/src/components/Accordion/AccordionBody.tsx +65 -0
  67. package/src/components/Accordion/AccordionHeader.tsx +68 -0
  68. package/src/components/Accordion/AccordionTitle.tsx +26 -0
  69. package/src/components/Accordion/variables.ts +56 -0
  70. package/src/components/Buttons/AIAssistantButton.tsx +141 -0
  71. package/src/components/Buttons/variables.dark.ts +7 -0
  72. package/src/components/Buttons/variables.ts +48 -0
  73. package/src/components/Catalog/Catalog.tsx +3 -2
  74. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  75. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  76. package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +56 -0
  77. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +169 -0
  78. package/src/components/Catalog/CatalogFilter/CatalogFilterContent.tsx +121 -0
  79. package/src/components/Catalog/CatalogFilter/CatalogFilterDateRange.tsx +147 -0
  80. package/src/components/Catalog/CatalogFilter/CatalogFilterSelect.tsx +136 -0
  81. package/src/components/Catalog/CatalogSelector.tsx +0 -1
  82. package/src/components/Catalog/variables.ts +0 -1
  83. package/src/components/Filter/FilterInput.tsx +3 -2
  84. package/src/components/Filter/FilterOptions.tsx +2 -0
  85. package/src/components/Filter/variables.ts +7 -4
  86. package/src/components/Search/SearchAiDialog.tsx +2 -2
  87. package/src/components/Search/SearchAiResponse.tsx +2 -2
  88. package/src/components/Search/SearchDialog.tsx +7 -2
  89. package/src/components/Tag/Tag.tsx +33 -8
  90. package/src/components/Tag/variables.dark.ts +135 -0
  91. package/src/components/Tag/variables.ts +120 -58
  92. package/src/core/hooks/use-tabs.ts +160 -238
  93. package/src/core/styles/dark.ts +11 -8
  94. package/src/core/styles/global.ts +7 -2
  95. package/src/core/types/l10n.ts +1 -0
  96. package/src/core/utils/index.ts +1 -0
  97. package/src/core/utils/tabs.ts +4 -0
  98. package/src/icons/RedoclyIcon/RedoclyIcon.tsx +44 -0
  99. package/src/index.ts +2 -0
  100. package/src/layouts/RootLayout.tsx +6 -0
  101. package/src/markdoc/components/Tabs/Tab.tsx +1 -0
  102. package/src/markdoc/components/Tabs/TabList.tsx +84 -30
  103. package/src/markdoc/components/Tabs/Tabs.tsx +12 -125
  104. package/src/markdoc/tags/tabs.ts +5 -0
@@ -0,0 +1,121 @@
1
+ import React from 'react';
2
+ import styled, { css } from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { ResolvedFilter } from '@redocly/theme/core/types';
6
+ import type { RedoclyConfig } from '@redocly/theme/config';
7
+
8
+ import { FilterInput } from '@redocly/theme/components/Filter/FilterInput';
9
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
10
+ import { Button } from '@redocly/theme/components/Button/Button';
11
+ import { CatalogFilter } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilter';
12
+ import { isFromToSelectedOptions } from '@redocly/theme/core/utils';
13
+
14
+ export type CatalogFilterContentProps = {
15
+ setFilterTerm: (value: string) => void;
16
+ filters: ResolvedFilter[];
17
+ filterTerm: string;
18
+ filterValuesCasing?:
19
+ | NonNullable<RedoclyConfig['catalog']>[string]['filterValuesCasing']
20
+ | ((str: string) => string);
21
+ hideSearch?: boolean;
22
+ showCounter?: boolean;
23
+ className?: string;
24
+ };
25
+
26
+ export function CatalogFilterContent({
27
+ setFilterTerm,
28
+ filters,
29
+ filterTerm,
30
+ filterValuesCasing,
31
+ hideSearch,
32
+ showCounter = true,
33
+ className,
34
+ }: CatalogFilterContentProps): JSX.Element | null {
35
+ const { useTranslate } = useThemeHooks();
36
+ const { translate } = useTranslate();
37
+
38
+ const hasActiveFilters = filters.some((filter) => {
39
+ if (filterTerm) return true;
40
+ if (filter.selectedOptions && filter.selectedOptions instanceof Set) {
41
+ return filter.selectedOptions.size;
42
+ } else if (
43
+ isFromToSelectedOptions(filter.selectedOptions) &&
44
+ filter.selectedOptions.from &&
45
+ filter.selectedOptions.to
46
+ ) {
47
+ return true;
48
+ }
49
+ });
50
+
51
+ const handleClearAll = (): void => {
52
+ filters.forEach((filter) => filter.selectOption(''));
53
+ setFilterTerm('');
54
+ };
55
+
56
+ if (!filters.length) {
57
+ return null;
58
+ }
59
+
60
+ return (
61
+ <CatalogFilterContentWrapper
62
+ className={className}
63
+ data-component-name="Catalog/CatalogFilterContent"
64
+ >
65
+ <FiltersHeader>
66
+ <FiltersTitle data-translation-key="catalog.filters.title">
67
+ {translate('catalog.filters.title', 'Filters')}
68
+ </FiltersTitle>
69
+ {hasActiveFilters ? (
70
+ <Button size="medium" tone="danger" variant="ghost" onClick={handleClearAll}>
71
+ {translate('catalog.filters.clearAll', 'Clear filters')}
72
+ </Button>
73
+ ) : null}
74
+ </FiltersHeader>
75
+ {!hideSearch && (
76
+ <FilterInput value={filterTerm} onChange={(updatedTerm) => setFilterTerm(updatedTerm)} />
77
+ )}
78
+ <FilterItems hideSearch={hideSearch}>
79
+ {filters.map((filter, idx) => (
80
+ <CatalogFilter
81
+ filter={filter}
82
+ key={filter.property + '-' + idx}
83
+ filterValuesCasing={filterValuesCasing}
84
+ showCounter={showCounter}
85
+ />
86
+ ))}
87
+ </FilterItems>
88
+ </CatalogFilterContentWrapper>
89
+ );
90
+ }
91
+
92
+ const CatalogFilterContentWrapper = styled.div`
93
+ padding: 0 0 var(--filter-content-padding-vertical) 0;
94
+ display: flex;
95
+ flex-direction: column;
96
+ `;
97
+
98
+ const FiltersHeader = styled.div`
99
+ display: flex;
100
+ padding: var(--filter-content-header-padding-vertical)
101
+ var(--filter-content-header-padding-horizontal);
102
+ `;
103
+
104
+ const FiltersTitle = styled.div`
105
+ margin-right: auto;
106
+ font-size: var(--filter-content-title-font-size);
107
+ font-weight: var(--filter-content-title-font-weight);
108
+ line-height: var(--filter-content-title-line-height);
109
+ `;
110
+
111
+ const FilterItems = styled.div<{ hideSearch?: boolean }>`
112
+ height: 100%;
113
+ display: flex;
114
+ flex-direction: column;
115
+ border-top: 1px solid var(--border-color-secondary);
116
+ ${({ hideSearch }) =>
117
+ hideSearch &&
118
+ css`
119
+ border-top: none;
120
+ `}
121
+ `;
@@ -0,0 +1,147 @@
1
+ import React, { useMemo } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { FilterProps } from '@redocly/theme/core/types';
6
+
7
+ import { FilterOptions } from '@redocly/theme/components/Filter/FilterOptions';
8
+ import { DatePicker } from '@redocly/theme/components/DatePicker/DatePicker';
9
+ import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
10
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
11
+ import { formatDateWithoutTimeZone } from '@redocly/theme/core/utils';
12
+ import { Accordion } from '@redocly/theme/components/Accordion/Accordion';
13
+
14
+ export type CatalogFilterDateRangeProps = FilterProps & {
15
+ className?: string;
16
+ };
17
+
18
+ export function CatalogFilterDateRange({
19
+ filter,
20
+ className,
21
+ }: CatalogFilterDateRangeProps): JSX.Element {
22
+ const { useTranslate } = useThemeHooks();
23
+ const { translate } = useTranslate();
24
+
25
+ const from = useMemo(
26
+ () => (filter.selectedOptions.from ? new Date(filter.selectedOptions.from) : undefined),
27
+ [filter.selectedOptions.from],
28
+ );
29
+ const to = useMemo(
30
+ () => (filter.selectedOptions.to ? new Date(filter.selectedOptions.to) : undefined),
31
+ [filter.selectedOptions.to],
32
+ );
33
+
34
+ const selectedCount = useMemo(() => {
35
+ return from && to ? 1 : 0;
36
+ }, [from, to]);
37
+
38
+ const headerContent = (
39
+ <AccordionHeaderContent>
40
+ <span>{translate(filter.titleTranslationKey, filter.title)}</span>
41
+ {selectedCount > 0 && <CounterTag borderless>{selectedCount}</CounterTag>}
42
+ </AccordionHeaderContent>
43
+ );
44
+
45
+ return (
46
+ <AccordionWrapper
47
+ className={className}
48
+ $hasSelection={selectedCount > 0}
49
+ data-component-name="Catalog/CatalogFilterDateRange"
50
+ >
51
+ <Accordion expanded={false} header={headerContent}>
52
+ <FilterOptions>
53
+ <DateRangeWrapper>
54
+ <DateRangeRow>
55
+ <span>From:</span>
56
+ <DatePicker
57
+ closeCalendar={true}
58
+ format="y-MM-dd"
59
+ dayPlaceholder="DD"
60
+ monthPlaceholder="MM"
61
+ yearPlaceholder="YYYY"
62
+ value={from}
63
+ maxDate={new Date()}
64
+ onChange={(from) => {
65
+ if (Array.isArray(from)) return;
66
+ filter.selectOption({
67
+ ...filter.selectedOptions,
68
+ from: formatDateWithoutTimeZone(from),
69
+ to: formatDateWithoutTimeZone(to),
70
+ });
71
+ }}
72
+ />
73
+ </DateRangeRow>
74
+ <DateRangeRow>
75
+ <span>To:</span>
76
+ <DatePicker
77
+ closeCalendar={true}
78
+ dayPlaceholder="DD"
79
+ monthPlaceholder="MM"
80
+ yearPlaceholder="YYYY"
81
+ format="y-MM-dd"
82
+ minDate={from}
83
+ value={to}
84
+ onChange={(to) => {
85
+ if (Array.isArray(to)) return;
86
+ filter.selectOption({
87
+ ...filter.selectedOptions,
88
+ from: formatDateWithoutTimeZone(from),
89
+ to: formatDateWithoutTimeZone(to),
90
+ });
91
+ }}
92
+ />
93
+ </DateRangeRow>
94
+ </DateRangeWrapper>
95
+ </FilterOptions>
96
+ </Accordion>
97
+ </AccordionWrapper>
98
+ );
99
+ }
100
+
101
+ const AccordionWrapper = styled.div<{ $hasSelection: boolean }>`
102
+ position: relative;
103
+ border-right: 4px solid transparent;
104
+
105
+ ${({ $hasSelection }) =>
106
+ $hasSelection &&
107
+ `border-right-color: var(--color-blueberry-6);
108
+
109
+ &::after {
110
+ content: '';
111
+ position: absolute;
112
+ bottom: -1px;
113
+ right: -4px;
114
+ width: 4px;
115
+ height: 1px;
116
+ background-color: var(--color-blueberry-6);
117
+ z-index: 1;
118
+ }
119
+ `}
120
+ `;
121
+
122
+ const AccordionHeaderContent = styled.div`
123
+ display: flex;
124
+ align-items: center;
125
+ gap: var(--spacing-xs);
126
+ width: 100%;
127
+ `;
128
+
129
+ const DateRangeWrapper = styled.div`
130
+ display: flex;
131
+ flex-direction: column;
132
+ gap: var(--filter-options-gap);
133
+ `;
134
+
135
+ const DateRangeRow = styled.div`
136
+ color: var(--filter-date-picker-color);
137
+ display: flex;
138
+ flex-direction: row;
139
+
140
+ align-items: center;
141
+ gap: var(--filter-date-picker-gap);
142
+
143
+ > span {
144
+ width: var(--filter-date-picker-width);
145
+ color: var(--filter-option-label-color);
146
+ }
147
+ `;
@@ -0,0 +1,136 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { FilterProps, SelectProps, SelectOption } from '@redocly/theme/core/types';
6
+
7
+ import { FilterOption } from '@redocly/theme/components/Filter/FilterOption';
8
+ import { FilterOptionLabel } from '@redocly/theme/components/Filter/FilterOptionLabel';
9
+ import { Select } from '@redocly/theme/components/Select/Select';
10
+ import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
11
+ import { changeTextCasing } from '@redocly/theme/core/utils';
12
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
13
+ import { Accordion } from '@redocly/theme/components/Accordion/Accordion';
14
+
15
+ export type CatalogFilterSelectProps = FilterProps & {
16
+ className?: string;
17
+ };
18
+
19
+ export function CatalogFilterSelect({
20
+ filter,
21
+ filterValuesCasing,
22
+ showCounter = true,
23
+ className,
24
+ }: CatalogFilterSelectProps): JSX.Element {
25
+ const { useTranslate } = useThemeHooks();
26
+ const { translate } = useTranslate();
27
+ const [isExpanded, setIsExpanded] = useState(false);
28
+
29
+ const selectedCount = useMemo(() => {
30
+ if (filter.selectedOptions instanceof Set) {
31
+ const firstValue = filter.selectedOptions.values().next()?.value;
32
+
33
+ return firstValue && firstValue !== '' ? 1 : 0;
34
+ }
35
+ return 0;
36
+ }, [filter.selectedOptions]);
37
+
38
+ let selectOptions: SelectOption<string>[] = [];
39
+
40
+ const defaultOptionCount = filter.options.reduce((acc, option) => acc + option.count, 0);
41
+
42
+ selectOptions = [
43
+ {
44
+ value: '',
45
+ element: (
46
+ <FilterOption key="all" onClick={() => filter.selectOption('')}>
47
+ <FilterOptionLabel data-translation-key="catalog.filters.select.all">
48
+ {translate('catalog.filters.select.all', 'All')}
49
+ </FilterOptionLabel>
50
+ <CounterTag borderless>{defaultOptionCount}</CounterTag>
51
+ </FilterOption>
52
+ ),
53
+ },
54
+ ...filter.options.map((option) => ({
55
+ value: option.value,
56
+ element: (
57
+ <FilterOption key={option.value}>
58
+ <FilterOptionLabel data-translation-key={option.value}>
59
+ {changeTextCasing(translate(option.value), filterValuesCasing)}
60
+ </FilterOptionLabel>
61
+ {showCounter && <CounterTag borderless>{option.count}</CounterTag>}
62
+ </FilterOption>
63
+ ),
64
+ })),
65
+ ];
66
+
67
+ const headerContent = (
68
+ <AccordionHeaderContent>
69
+ <span>{translate(filter.titleTranslationKey, filter.title)}</span>
70
+ {selectedCount > 0 && <CounterTag borderless>{selectedCount}</CounterTag>}
71
+ </AccordionHeaderContent>
72
+ );
73
+
74
+ return (
75
+ <AccordionWrapper
76
+ className={className}
77
+ $hasSelection={selectedCount > 0 && !isExpanded}
78
+ data-component-name="Catalog/CatalogFilterSelect"
79
+ >
80
+ <Accordion expanded={isExpanded} header={headerContent} onToggle={setIsExpanded}>
81
+ <StyledSelect
82
+ value={
83
+ (filter.selectedOptions instanceof Set ? filter.selectedOptions : new Set())
84
+ .values()
85
+ .next()?.value || ''
86
+ }
87
+ onChange={(value) => filter.selectOption(value)}
88
+ options={selectOptions}
89
+ />
90
+ </Accordion>
91
+ </AccordionWrapper>
92
+ );
93
+ }
94
+
95
+ const AccordionWrapper = styled.div<{ $hasSelection: boolean }>`
96
+ position: relative;
97
+ border-right: 4px solid transparent;
98
+
99
+ ${({ $hasSelection }) =>
100
+ $hasSelection &&
101
+ `border-right-color: var(--color-blueberry-6);
102
+
103
+ &::after {
104
+ content: '';
105
+ position: absolute;
106
+ bottom: -1px;
107
+ right: -4px;
108
+ width: 4px;
109
+ height: 1px;
110
+ background-color: var(--color-blueberry-6);
111
+ z-index: 1;
112
+ }
113
+ `}
114
+ `;
115
+
116
+ const AccordionHeaderContent = styled.div`
117
+ display: flex;
118
+ align-items: center;
119
+ gap: var(--spacing-xs);
120
+ width: 100%;
121
+ `;
122
+
123
+ const StyledSelect = styled(Select)<SelectProps>`
124
+ --select-list-max-width: var(--filter-select-max-width);
125
+
126
+ min-height: var(--filter-select-min-height);
127
+ --select-text-color: var(--filter-select-color);
128
+ border: 1px solid var(--filter-select-border-color);
129
+ --select-border-radius: var(--filter-select-border-radius);
130
+ padding: var(--filter-select-padding);
131
+
132
+ :hover {
133
+ border-color: var(--filter-select-border-color-hover);
134
+ }
135
+ --filter-option-margin: var(--filter-select-option-margin);
136
+ `;
@@ -52,7 +52,6 @@ export function CatalogSelector({
52
52
 
53
53
  export const CatalogSelectWrapper = styled.div`
54
54
  margin-top: var(--catalog-select-wrapper-margin-top);
55
- margin-bottom: var(--catalog-select-wrapper-margin-bottom);
56
55
  `;
57
56
 
58
57
  export const CatalogSelectLabel = styled.label`
@@ -80,7 +80,6 @@ export const catalog = css`
80
80
  * @tokens Catalog selector
81
81
  */
82
82
  --catalog-select-wrapper-margin-top: var(--spacing-xxs);
83
- --catalog-select-wrapper-margin-bottom: var(--spacing-base);
84
83
  --catalog-select-label-margin-bottom: var(--spacing-xxs);
85
84
  --catalog-select-label-font-weight: var(--filter-title-font-weight);
86
85
  --catalog-select-label-font-size: var(--filter-title-font-size);
@@ -9,10 +9,11 @@ import { SearchIcon } from '@redocly/theme/icons/SearchIcon/SearchIcon';
9
9
  export type FilterInputProps = {
10
10
  value: string;
11
11
  onChange: (newValue: string) => void;
12
+ dataTestId?: string;
12
13
  };
13
14
 
14
15
  export function FilterInput(props: FilterInputProps): JSX.Element {
15
- const { value: initialValue, onChange } = props;
16
+ const { value: initialValue, onChange, dataTestId } = props;
16
17
 
17
18
  const [value, setValue] = useState(initialValue);
18
19
 
@@ -30,7 +31,7 @@ export function FilterInput(props: FilterInputProps): JSX.Element {
30
31
  const { translate } = useTranslate();
31
32
 
32
33
  return (
33
- <InputWrapper data-component-name="Filter/FilterInput">
34
+ <InputWrapper data-component-name="Filter/FilterInput" data-testid={dataTestId}>
34
35
  <InputIcon />
35
36
  <Input
36
37
  value={value}
@@ -16,4 +16,6 @@ const FilterOptionsComponent = styled.div`
16
16
  display: flex;
17
17
  flex-direction: column;
18
18
  gap: var(--filter-options-gap);
19
+ max-height: var(--filter-options-max-height);
20
+ overflow-y: scroll;
19
21
  `;
@@ -15,6 +15,7 @@ export const filter = css`
15
15
  --filter-options-padding-vertical: 0;
16
16
  --filter-options-padding-horizontal: 0;
17
17
  --filter-options-gap: var(--spacing-sm);
18
+ --filter-options-max-height: 350px;
18
19
 
19
20
  --filter-option-font-size: var(--font-size-base);
20
21
  --filter-option-gap: var(--spacing-xs);
@@ -25,12 +26,14 @@ export const filter = css`
25
26
 
26
27
  --filter-option-checkbox-padding-left: var(--spacing-xs);
27
28
 
28
- --filter-content-header-padding-vertical: 5px;
29
- --filter-content-header-padding-horizontal: 0;
29
+ --filter-content-header-padding-vertical: var(--spacing-xs) var(--spacing-sm);
30
+ --filter-content-header-padding-horizontal: var(--spacing-base);
30
31
 
31
- --filter-content-title-font-size: var(--font-size-base);
32
- --filter-content-title-line-height: var(--line-height-base);
32
+ --filter-content-title-font-size: var(--font-size-lg);
33
+ --filter-content-title-line-height: var(--line-height-lg);
33
34
  --filter-content-title-font-weight: var(--font-weight-semibold);
35
+
36
+ --filter-content-search-padding: 0 var(--spacing-xs) var(--spacing-sm);
34
37
 
35
38
  --filter-content-padding-vertical: var(--spacing-sm);
36
39
  --filter-content-padding-horizontal: var(--spacing-base);
@@ -43,12 +43,12 @@ export function SearchAiDialog({
43
43
  setConversation,
44
44
  }: SearchAiDialogProps): JSX.Element {
45
45
  const { useTranslate } = useThemeHooks();
46
- const { search } = useThemeConfig();
46
+ const { aiAssistant } = useThemeConfig();
47
47
  const { translate } = useTranslate();
48
48
 
49
49
  const conversationEndRef = React.useRef<HTMLDivElement>(null);
50
50
 
51
- const suggestions = search?.ai?.suggestions;
51
+ const suggestions = aiAssistant?.suggestions;
52
52
 
53
53
  const placeholder = isGeneratingResponse
54
54
  ? translate('search.ai.generatingResponse', 'Generating response...')
@@ -34,7 +34,7 @@ export function SearchAiResponse(props: SearchAiResponseProps): JSX.Element {
34
34
  const { useMarkdownText } = useThemeHooks();
35
35
  const { question, response, isGeneratingResponse, resources, error, onSuggestionClick } = props;
36
36
 
37
- const { search } = useThemeConfig();
37
+ const { aiAssistant } = useThemeConfig();
38
38
  const { useTranslate } = useThemeHooks();
39
39
 
40
40
  const { translate } = useTranslate();
@@ -42,7 +42,7 @@ export function SearchAiResponse(props: SearchAiResponseProps): JSX.Element {
42
42
 
43
43
  let responseContainer = null;
44
44
 
45
- const suggestions = search?.ai?.suggestions;
45
+ const suggestions = aiAssistant?.suggestions;
46
46
 
47
47
  const hasPendingOrReceivedResponse = response || isGeneratingResponse || error;
48
48
  if (hasPendingOrReceivedResponse) {
@@ -34,16 +34,21 @@ import { AiStarsGradientIcon } from '@redocly/theme/icons/AiStarsGradientIcon/Ai
34
34
  export type SearchDialogProps = {
35
35
  onClose: () => void;
36
36
  className?: string;
37
+ initialMode?: 'search' | 'ai-dialog';
37
38
  };
38
39
 
39
- export function SearchDialog({ onClose, className }: SearchDialogProps): JSX.Element {
40
+ export function SearchDialog({
41
+ onClose,
42
+ className,
43
+ initialMode = 'search',
44
+ }: SearchDialogProps): JSX.Element {
40
45
  const { useTranslate, useCurrentProduct, useSearch, useProducts, useAiSearch, useTelemetry } =
41
46
  useThemeHooks();
42
47
  const telemetry = useTelemetry();
43
48
  const products = useProducts();
44
49
  const currentProduct = useCurrentProduct();
45
50
  const [product, setProduct] = useState(currentProduct);
46
- const [mode, setMode] = useState<'search' | 'ai-dialog'>('search');
51
+ const [mode, setMode] = useState<'search' | 'ai-dialog'>(initialMode);
47
52
  const autoSearchDisabled = mode !== 'search';
48
53
  const {
49
54
  query,
@@ -26,13 +26,16 @@ type Color =
26
26
  | 'green'
27
27
  | 'blue'
28
28
  | 'grey'
29
- | 'gold'
30
- | 'cyan'
29
+ | 'turquoise'
31
30
  | 'magenta'
32
31
  | 'purple'
33
- | 'lime'
34
- | 'geekblue'
35
- | 'yellow';
32
+ | 'carrot'
33
+ | 'raspberry'
34
+ | 'orange'
35
+ | 'grass'
36
+ | 'persian-green'
37
+ | 'sky'
38
+ | 'blueberry';
36
39
 
37
40
  export type TagProps = {
38
41
  className?: string;
@@ -52,6 +55,7 @@ export type TagProps = {
52
55
  onClose?: (event: React.MouseEvent) => void;
53
56
  maxLength?: number;
54
57
  textTransform?: 'uppercase' | 'lowercase' | 'capitalize' | 'none';
58
+ variant?: 'outline' | 'filled';
55
59
  };
56
60
 
57
61
  export function Tag({
@@ -70,6 +74,7 @@ export function Tag({
70
74
  statusDotColor = 'var(--tag-status-dot-color-default)',
71
75
  maxLength,
72
76
  textTransform,
77
+ variant = 'filled',
73
78
  ...otherProps
74
79
  }: TagProps): JSX.Element {
75
80
  const truncateText = (text: string, maxLen: number): string => {
@@ -146,6 +151,7 @@ export function Tag({
146
151
  onKeyDown={onKeyDown}
147
152
  hasCloseButton={closable}
148
153
  textTransform={textTransform}
154
+ variant={variant}
149
155
  {...otherProps}
150
156
  >
151
157
  {withStatusDot ? <StatusDot color={statusDotColor} /> : icon ? icon : null}
@@ -221,12 +227,31 @@ const TagWrapper = styled.div.attrs(({ className, color, size }: TagProps) => ({
221
227
  `text-transform: ${textTransform ? `${textTransform}` : 'var(--tag-text-transform)'};`}
222
228
 
223
229
  color: var(--tag-color);
224
- background-color: var(--tag-bg-color);
225
- ${({ borderless }) =>
230
+ background-color: ${({ variant }) =>
231
+ variant === 'filled' ? 'var(--tag-bg-color)' : 'transparent'};
232
+ ${({ borderless, variant }) =>
226
233
  borderless
227
234
  ? ''
228
- : 'border: var(--tag-border-width) var(--tag-border-style) var(--tag-border-color);'}
235
+ : `border: var(--tag-border-width) var(--tag-border-style) ${variant === 'filled' ? 'transparent' : 'var(--tag-border-color)'};`}
229
236
  border-radius: var(--tag-border-radius);
237
+
238
+ svg {
239
+ width: var(--tag-icon-width);
240
+ height: var(--tag-icon-height);
241
+ }
242
+
243
+ &:hover {
244
+ background-color: ${({ variant }) =>
245
+ variant === 'filled' ? 'var(--tag-bg-color-hover)' : 'transparent'};
246
+ border-color: ${({ variant }) =>
247
+ variant === 'outline' ? 'var(--tag-border-color-hover)' : 'transparent'};
248
+ }
249
+
250
+ &:focus-visible {
251
+ outline: 1px solid var(--tag-border-color-focused);
252
+ outline-offset: 2px;
253
+ border-radius: var(--tag-border-radius-focused);
254
+ }
230
255
  `;
231
256
 
232
257
  const StatusDot = styled.div<{ color: string }>`