Skip to content

Commit dea9068

Browse files
fix(DashboardEditor): CSS template selector UI in dashboard properties modal restored (#35106)
1 parent 3416bd1 commit dea9068

File tree

4 files changed

+274
-56
lines changed

4 files changed

+274
-56
lines changed

superset-frontend/package-lock.json

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

superset-frontend/src/dashboard/components/PropertiesModal/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ const PropertiesModal = ({
715715
onThemeChange={handleThemeChange}
716716
onColorSchemeChange={onColorSchemeChange}
717717
onCustomCssChange={setCustomCss}
718+
addDangerToast={addDangerToast}
718719
/>
719720
),
720721
},

superset-frontend/src/dashboard/components/PropertiesModal/sections/StylingSection.test.tsx

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,29 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
import { render, screen, userEvent } from 'spec/helpers/testing-library';
19+
import {
20+
render,
21+
screen,
22+
userEvent,
23+
waitFor,
24+
} from 'spec/helpers/testing-library';
25+
import { SupersetClient, isFeatureEnabled } from '@superset-ui/core';
2026
import StylingSection from './StylingSection';
2127

28+
// Mock SupersetClient
29+
jest.mock('@superset-ui/core', () => ({
30+
...jest.requireActual('@superset-ui/core'),
31+
SupersetClient: {
32+
get: jest.fn(),
33+
},
34+
isFeatureEnabled: jest.fn(),
35+
}));
36+
37+
const mockSupersetClient = SupersetClient as jest.Mocked<typeof SupersetClient>;
38+
const mockIsFeatureEnabled = isFeatureEnabled as jest.MockedFunction<
39+
typeof isFeatureEnabled
40+
>;
41+
2242
// Mock ColorSchemeSelect component
2343
jest.mock('src/dashboard/components/ColorSchemeSelect', () => ({
2444
__esModule: true,
@@ -33,6 +53,14 @@ jest.mock('src/dashboard/components/ColorSchemeSelect', () => ({
3353
),
3454
}));
3555

56+
const mockCssTemplates = [
57+
{ template_name: 'Corporate Blue', css: '.dashboard { background: blue; }' },
58+
{
59+
template_name: 'Modern Dark',
60+
css: '.dashboard { background: black; color: white; }',
61+
},
62+
];
63+
3664
const defaultProps = {
3765
themes: [
3866
{ id: 1, theme_name: 'Dark Theme' },
@@ -45,10 +73,17 @@ const defaultProps = {
4573
onThemeChange: jest.fn(),
4674
onColorSchemeChange: jest.fn(),
4775
onCustomCssChange: jest.fn(),
76+
addDangerToast: jest.fn(),
4877
};
4978

5079
beforeEach(() => {
5180
jest.clearAllMocks();
81+
// Reset mocks
82+
mockIsFeatureEnabled.mockReturnValue(false);
83+
mockSupersetClient.get.mockResolvedValue({
84+
json: { result: mockCssTemplates },
85+
response: {} as Response,
86+
});
5287
});
5388

5489
test('renders theme selection when themes are available', () => {
@@ -120,3 +155,76 @@ test('displays current color scheme value', () => {
120155
const colorSchemeInput = screen.getByLabelText('Select color scheme');
121156
expect(colorSchemeInput).toHaveValue('testColors');
122157
});
158+
159+
// CSS Template Tests
160+
describe('CSS Template functionality', () => {
161+
test('does not show CSS template select when feature flag is disabled', () => {
162+
mockIsFeatureEnabled.mockReturnValue(false);
163+
render(<StylingSection {...defaultProps} />);
164+
165+
expect(
166+
screen.queryByTestId('dashboard-css-template-field'),
167+
).not.toBeInTheDocument();
168+
});
169+
170+
test('fetches CSS templates on mount when feature enabled', async () => {
171+
mockIsFeatureEnabled.mockImplementation(flag => flag === 'CSS_TEMPLATES');
172+
render(<StylingSection {...defaultProps} />);
173+
174+
await waitFor(() => {
175+
expect(mockSupersetClient.get).toHaveBeenCalledWith({
176+
endpoint: expect.stringContaining('/api/v1/css_template/'),
177+
});
178+
});
179+
});
180+
181+
test('shows CSS template select when feature flag is enabled and templates exist', async () => {
182+
mockIsFeatureEnabled.mockImplementation(flag => flag === 'CSS_TEMPLATES');
183+
render(<StylingSection {...defaultProps} />);
184+
185+
await waitFor(() => {
186+
expect(
187+
screen.getByText('Load CSS template (optional)'),
188+
).toBeInTheDocument();
189+
});
190+
191+
expect(
192+
screen.getByTestId('dashboard-css-template-select'),
193+
).toBeInTheDocument();
194+
});
195+
196+
test('shows error toast when template fetch fails', async () => {
197+
mockIsFeatureEnabled.mockImplementation(flag => flag === 'CSS_TEMPLATES');
198+
const addDangerToast = jest.fn();
199+
mockSupersetClient.get.mockRejectedValueOnce(new Error('API Error'));
200+
201+
render(
202+
<StylingSection {...defaultProps} addDangerToast={addDangerToast} />,
203+
);
204+
205+
await waitFor(() => {
206+
expect(addDangerToast).toHaveBeenCalledWith(
207+
'An error occurred while fetching available CSS templates',
208+
);
209+
});
210+
});
211+
212+
test('does not show CSS template select when no templates available', async () => {
213+
mockIsFeatureEnabled.mockImplementation(flag => flag === 'CSS_TEMPLATES');
214+
mockSupersetClient.get.mockResolvedValueOnce({
215+
json: { result: [] },
216+
response: {} as Response,
217+
});
218+
219+
render(<StylingSection {...defaultProps} />);
220+
221+
// Wait for fetch to complete
222+
await waitFor(() => {
223+
expect(mockSupersetClient.get).toHaveBeenCalled();
224+
});
225+
226+
expect(
227+
screen.queryByTestId('dashboard-css-template-field'),
228+
).not.toBeInTheDocument();
229+
});
230+
});

0 commit comments

Comments
 (0)