Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions superset-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion superset-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
"@rjsf/utils": "^5.24.3",
"@rjsf/validator-ajv8": "^5.24.13",
"@scarf/scarf": "^1.4.0",
"@stacklet/ui": "^2.16.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
"@superset-ui/legacy-plugin-chart-calendar": "file:./plugins/legacy-plugin-chart-calendar",
Expand All @@ -146,7 +147,6 @@
"@superset-ui/plugin-chart-pivot-table": "file:./plugins/plugin-chart-pivot-table",
"@superset-ui/plugin-chart-table": "file:./plugins/plugin-chart-table",
"@superset-ui/plugin-chart-word-cloud": "file:./plugins/plugin-chart-word-cloud",
"@stacklet/ui": "^2.15.0",
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
"@types/d3-format": "^3.0.1",
"@types/d3-selection": "^3.0.11",
Expand Down
19 changes: 16 additions & 3 deletions superset-frontend/src/stacklet/StackletSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ import '@fontsource-variable/dm-sans';

import getBootstrapData from 'src/utils/getBootstrapData';
import type { MenuData } from 'src/types/bootstrapTypes';
import { getAppSelectorOptions, SUPERSET_APP_NAME } from './appSelector';
import { getAppSelectorOptions, getSupersetAppName } from './appSelector';
import { buildNavItems, resolveActiveNavToken } from './menuItems';
// The design system's stylesheet with every rule pre-scoped under
// `.stacklet-ui-scope` — the raw dist/ui.css would restyle all of Superset
// (Tailwind preflight + global html/body rules).
import '@stacklet/ui/dist/ui.scoped.css';

// Superset-local layout/type tweaks so "AssetDB (Preview)" fits the sidebar;
// must come after the scoped stylesheet to win the cascade.
import './stackletSidebarOverrides.css';

/** Matches the sidebar widths baked into the V2Sidebar component. */
export const SIDEBAR_WIDTH = '13.625rem';
export const SIDEBAR_COLLAPSED_WIDTH = '4.5rem';
Expand Down Expand Up @@ -258,6 +262,7 @@ export default function StackletSidebar({
[data, user],
);
const appSelectorOptions = useMemo(() => getAppSelectorOptions(), []);
const supersetAppName = useMemo(() => getSupersetAppName(), []);
const signInMenu = useCurrentUserMenu(data, collapsed, themeControl);
// See resolveActiveNavToken: a virtual token stands in for the pathname so
// exactly one nav item is highlighted, query-aware for filtered presets.
Expand All @@ -266,8 +271,16 @@ export default function StackletSidebar({
[navItems, pathname, search],
);

// TEMPORARY — remove together with stackletSidebarOverrides.css once the
// Redash-backed AssetDB is retired: only the longer "AssetDB (Preview)"
// branding needs the sizing overrides that class scopes.
const scopeClassName =
supersetAppName === 'AssetDB (Preview)'
? 'stacklet-ui-scope stacklet-preview-branding'
: 'stacklet-ui-scope';

return (
<div className="stacklet-ui-scope" data-theme={scopeTheme} ref={scopeRef}>
<div className={scopeClassName} data-theme={scopeTheme} ref={scopeRef}>
<PortalProvider getContainer={getPortalContainer}>
<Sidebar
appSelectorOptions={appSelectorOptions}
Expand All @@ -276,7 +289,7 @@ export default function StackletSidebar({
navItems={navItems}
navigate={navigate}
pathname={activeToken}
selectedApp={SUPERSET_APP_NAME}
selectedApp={supersetAppName}
SignInMenu={signInMenu}
variant="grouped"
/>
Expand Down
41 changes: 32 additions & 9 deletions superset-frontend/src/stacklet/appSelector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/
import getBootstrapData from 'src/utils/getBootstrapData';
import { getAppSelectorOptions, SUPERSET_APP_NAME } from './appSelector';
import { getAppSelectorOptions, getSupersetAppName } from './appSelector';

jest.mock('src/utils/getBootstrapData', () => jest.fn());

Expand All @@ -26,18 +26,25 @@ const mockedBootstrap = getBootstrapData as jest.Mock;
const bootstrapWith = (common: object) =>
mockedBootstrap.mockReturnValue({ common });

test('builds entries from the platform-injected stacklet.urls', () => {
test('brands itself "AssetDB (Preview)" while the Redash-backed AssetDB exists', () => {
bootstrapWith({
stacklet: {
urls: {
console: 'https://console.acme.stacklet.io',
redash: 'https://redash.acme.stacklet.io',
sinistral: 'https://sinistral.acme.stacklet.io',
superset: 'https://superset.acme.stacklet.io',
},
},
});
expect(getSupersetAppName()).toBe('AssetDB (Preview)');
expect(getAppSelectorOptions()).toEqual([
{ label: SUPERSET_APP_NAME, href: '', isBeta: false },
{ label: 'AssetDB (Preview)', href: '', isBeta: false },
{
label: 'AssetDB',
href: 'https://redash.acme.stacklet.io',
isBeta: false,
},
{
label: 'Console',
href: 'https://console.acme.stacklet.io',
Expand All @@ -47,20 +54,36 @@ test('builds entries from the platform-injected stacklet.urls', () => {
]);
});

test('omits siblings whose URL is not configured', () => {
test('brands itself plain "AssetDB" when Redash is not deployed', () => {
bootstrapWith({
stacklet: { urls: { console: 'https://console.acme.stacklet.io' } },
stacklet: {
urls: {
console: 'https://console.acme.stacklet.io',
superset: 'https://superset.acme.stacklet.io',
},
},
});
expect(getAppSelectorOptions().map(option => option.label)).toEqual([
SUPERSET_APP_NAME,
'Console',
expect(getSupersetAppName()).toBe('AssetDB');
expect(getAppSelectorOptions()).toEqual([
{ label: 'AssetDB', href: '', isBeta: false },
{
label: 'Console',
href: 'https://console.acme.stacklet.io',
isBeta: false,
},
]);
});

test('falls back to the dev platform when the payload has no stacklet.urls', () => {
bootstrapWith({});
// The dev fallback includes a Redash URL, so the Preview name applies
expect(getAppSelectorOptions()).toEqual([
{ label: SUPERSET_APP_NAME, href: '', isBeta: false },
{ label: 'AssetDB (Preview)', href: '', isBeta: false },
{
label: 'AssetDB',
href: 'https://redash.dev.stacklet.dev',
isBeta: false,
},
{
label: 'Console',
href: 'https://console.dev.stacklet.dev',
Expand Down
38 changes: 26 additions & 12 deletions superset-frontend/src/stacklet/appSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@
*/
import getBootstrapData from 'src/utils/getBootstrapData';

/**
* How this Superset deployment is branded in the Stacklet platform's app
* switcher (Design Kit v2, node 6820-6796). Also part of @stacklet/ui's
* `V2AppName` union, so it can be passed to the Sidebar without casts.
*/
export const SUPERSET_APP_NAME = 'AssetDB';

export interface StackletAppOption {
label: string;
href: string;
Expand All @@ -50,19 +43,40 @@ interface StackletBootstrapExtras {
*/
const DEV_FALLBACK_URLS: Partial<Record<string, string>> = {
console: 'https://console.dev.stacklet.dev',
redash: 'https://redash.dev.stacklet.dev',
sinistral: 'https://sinistral.dev.stacklet.dev',
};

function getStackletUrls(): Partial<Record<string, string>> {
const common = getBootstrapData().common as StackletBootstrapExtras;
return common?.stacklet?.urls ?? DEV_FALLBACK_URLS;
}

/**
* How this Superset deployment is branded in the Stacklet platform's app
* switcher (Design Kit v2, node 6820-6796). Plain "AssetDB" once it is the
* only AssetDB in the environment; "(Preview)" while the Redash-backed
* AssetDB is still deployed alongside it. Both names are part of
* @stacklet/ui's `V2AppName` union, so the result can be passed to the
* Sidebar without casts.
*/
export function getSupersetAppName(): 'AssetDB' | 'AssetDB (Preview)' {
return getStackletUrls().redash ? 'AssetDB (Preview)' : 'AssetDB';
}

/**
* Builds the app switcher entries for the Stacklet sidebar. Superset itself
* (branded AssetDB) is always present and selected; siblings appear only
* when their URL is configured.
* is always present and selected; siblings — including the Redash-backed
* "AssetDB" — appear only when their URL is configured. The Sidebar renders
* the selected app first regardless of this order.
*/
export function getAppSelectorOptions(): StackletAppOption[] {
const common = getBootstrapData().common as StackletBootstrapExtras;
const urls = common?.stacklet?.urls ?? DEV_FALLBACK_URLS;
const urls = getStackletUrls();
return [
{ label: SUPERSET_APP_NAME, href: '', isBeta: false },
{ label: getSupersetAppName(), href: '', isBeta: false },
...(urls.redash
? [{ label: 'AssetDB', href: urls.redash, isBeta: false }]
: []),
...(urls.console
? [{ label: 'Console', href: urls.console, isBeta: false }]
: []),
Expand Down
64 changes: 64 additions & 0 deletions superset-frontend/src/stacklet/stackletSidebarOverrides.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/*
* TEMPORARY — delete this file (and its import plus the
* `stacklet-preview-branding` class in StackletSidebar.tsx) once the
* Redash-backed AssetDB is retired: the app is then branded plain "AssetDB",
* which fits the sidebar with the component defaults.
*
* Superset-local tweaks on top of @stacklet/ui's scoped stylesheet (imported
* right before this file in StackletSidebar.tsx), so the longer
* "AssetDB (Preview)" pill fits the sidebar without touching the shared
* component. `stacklet-preview-branding` is stamped on the scope wrapper only
* while the Redash URL is configured — the only case that gets the long name.
* Selectors anchor on the trigger's aria-label rather than the dist's utility
* class names, which are minified build artifacts.
*/

/* Header row holding the logo and the app selector: tighter padding and gap
than the component defaults, freeing width for the longer name. */
.stacklet-ui-scope.stacklet-preview-branding
div:has(> div > button[aria-label^='App selector']) {
padding: 16px;
column-gap: 6px;
}

/* Slightly smaller logo, matching the reduced type next to it. */
.stacklet-ui-scope.stacklet-preview-branding
div:has(> div > button[aria-label^='App selector'])
> a
> svg {
width: 24px;
height: auto;
}

/* The trigger lays out label and tile icon on the text baseline; with the
smaller type below the icon rides too high, so center-align instead. */
.stacklet-ui-scope.stacklet-preview-branding
button[aria-label^='App selector'] {
align-items: center;
}

/* Smaller label type so the full name fits the expanded sidebar width. */
.stacklet-ui-scope.stacklet-preview-branding
button[aria-label^='App selector']
.whitespace-nowrap {
font-size: 12px;
}
Loading