codeant-ai-for-open-source[bot] commented on code in PR #44276:
URL: https://github.com/apache/superset/pull/44276#discussion_r4011808909


##########
superset-frontend/packages/superset-core/src/dashboard/index.ts:
##########
@@ -0,0 +1,196 @@
+/**
+ * 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.
+ */
+
+/**
+ * @fileoverview Dashboard API for Superset extensions.
+ *
+ * Exposes the dashboard currently active on the Dashboard surface (see
+ * `navigation.getPage() === 'dashboard'`) so extensions can identify it and
+ * read/apply its layout, custom CSS, and native filter values.
+ */
+
+/**
+ * Gets the ID of the dashboard currently active on the Dashboard surface.
+ *
+ * @returns The current dashboard's ID, or undefined if none is active.
+ *
+ * @example
+ * ```typescript
+ * const dashboardId = dashboard.getDashboardId();
+ * if (dashboardId != null) {
+ *   console.log(`Dashboard ID: ${dashboardId}`);
+ * }
+ * ```
+ */
+export declare function getDashboardId(): number | undefined;
+
+/**
+ * Gets the current dashboard's full layout tree — one entry per component
+ * (row, column, chart holder, tab, markdown, etc.), keyed by node ID. Each
+ * entry has `children`, `parents`, `type`, `id`, and `meta` (grid
+ * size/position and other component-specific settings).
+ *
+ * @returns A map of node ID to layout node.
+ *
+ * @example
+ * ```typescript
+ * const layout = dashboard.getLayout();
+ * console.log(layout['CHART-abc123'].meta.width);
+ * ```
+ */
+export declare function getLayout(): Record<string, unknown>;

Review Comment:
   Yes. The public API should export a `LayoutNode` type and use it as the 
value type for `getLayout()`. Since `meta` is component-specific, it can remain 
extensible while exposing common grid fields such as `width` and `height`.
   
   ```typescript
   export interface LayoutNodeMeta {
     width?: number;
     height?: number;
     position?: number;
     [key: string]: unknown;
   }
   
   export interface LayoutNode {
     id: string;
     type: string;
     children?: string[];
     parents?: string[];
     meta: LayoutNodeMeta;
     [key: string]: unknown;
   }
   
   export declare function getLayout(): Record<string, LayoutNode>;
   ```
   
   The example will then type-check:
   
   ```typescript
   const layout = dashboard.getLayout();
   const width = layout['CHART-abc123'].meta.width;
   ```
   
   The host implementation already returns the existing layout entries, so no 
runtime change is required; only the contract and return type need updating. 
This is preferable to leaving entries as `unknown`, since the documented shape 
is already part of the API’s intended usage.



##########
superset-frontend/src/core/dashboard/index.ts:
##########
@@ -0,0 +1,105 @@
+/**
+ * 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.
+ */
+import { dashboard as dashboardApi } from '@apache-superset/core';
+import { isNativeFilter } from '@superset-ui/core';
+import type { Divider, Filter } from '@superset-ui/core';
+import { updateComponents } from 'src/dashboard/actions/dashboardLayout';
+import { dashboardInfoChanged } from 'src/dashboard/actions/dashboardInfo';
+import { updateDataMask } from 'src/dataMask/actions';
+import { store, RootState } from 'src/views/store';
+
+const getState = () => store.getState() as RootState;
+
+const requireDashboardId = (): number => {
+  const { id } = getState().dashboardInfo;
+  if (id == null) {
+    throw new Error('No dashboard is currently active');
+  }
+  return id;
+};
+
+const getDashboardId: typeof dashboardApi.getDashboardId = () =>
+  getState().dashboardInfo.id ?? undefined;
+
+const getLayout: typeof dashboardApi.getLayout = () => ({
+  ...(getState().dashboardLayout.present as Record<string, unknown>),
+});
+
+const updateLayoutNode: typeof dashboardApi.updateLayoutNode = async (
+  nodeId: string,
+  meta: Record<string, unknown>,
+) => {
+  requireDashboardId();
+  const node = getState().dashboardLayout.present[nodeId];
+  if (!node) {
+    throw new Error(`Layout node "${nodeId}" not found`);
+  }
+  // UPDATE_COMPONENTS replaces each keyed entry wholesale (it's not a deep
+  // merge), so the node's other fields must be carried through alongside
+  // the merged meta.
+  store.dispatch(
+    updateComponents({
+      [nodeId]: { ...node, meta: { ...node.meta, ...meta } },
+    }) as any,
+  );
+};
+
+const getCss: typeof dashboardApi.getCss = () => getState().dashboardInfo.css 
?? '';
+
+const setCss: typeof dashboardApi.setCss = async (css: string) => {
+  requireDashboardId();
+  store.dispatch(dashboardInfoChanged({ css }));
+};
+
+const getFilters: typeof dashboardApi.getFilters = () => {
+  const { nativeFilters, dataMask } = getState();
+  const filterElements = Object.values(nativeFilters.filters) as Array<
+    Filter | Divider
+  >;
+  return filterElements.filter(isNativeFilter).map(filter => {
+    const mask = dataMask[filter.id];
+    return {
+      id: filter.id,
+      name: filter.name,
+      filterType: filter.filterType,
+      targets: filter.targets,
+      extraFormData: mask?.extraFormData,
+      filterState: mask?.filterState,
+    };
+  });
+};
+
+const updateFilters: typeof dashboardApi.updateFilters = async (
+  updates: dashboardApi.FilterValueUpdate[],
+) => {
+  requireDashboardId();
+  updates.forEach(({ filterId, extraFormData, filterState }) => {
+    store.dispatch(updateDataMask(filterId, { extraFormData, filterState }));
+  });

Review Comment:
   Yes. Build the dispatch payload conditionally so omitted fields are not 
passed as `undefined`; explicit empty objects remain valid updates.
   
   ```typescript
   updates.forEach(({ filterId, extraFormData, filterState }) => {
     const dataMask: {
       extraFormData?: Record<string, unknown>;
       filterState?: Record<string, unknown>;
     } = {};
   
     if (extraFormData !== undefined) {
       dataMask.extraFormData = extraFormData;
     }
     if (filterState !== undefined) {
       dataMask.filterState = filterState;
     }
   
     store.dispatch(updateDataMask(filterId, dataMask));
   });
   ```
   
   This preserves the existing `extraFormData` for a `filterState`-only update, 
and vice versa, while still allowing callers to intentionally provide empty 
values.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to