AdapTable Version:
Framework:
Summary
- The No Code Wizard can be configured so that developers are able to provide custom data adapters
- This provides unlimited flexibilty over where the data is dynamically generated
By default the first Step in the No Code Wizard enables users to drag and drop JSON and Excel files.
However, this can be overridden so that a custom data source step is provided.
Deep Dive
Customising the No Code Wizard
This section is coming soon...
You will need to provide an implementation of AdaptableOptionsWizardView
- This demo overrides the default First Step of the No Code Wizard with a Custom Step which provides 2 Data Sources to select from
- Both data sources, when selected, provide Column Defs for the second Wizard step (which is standard) but there is one interesting difference how these are derived:
Cars- the data source contains column definitions which are simply extracted when it is selectedAthletes- the data source doesn't contain definitions so these are dynamically generated
Expand to see how the first step of the No Code Wizard was created
The are 3 main steps involved:
- DataGrid.tsx contains the No-Code wizard which is wrapped in a
DataGridcomponent
export const DataGrid: React.FunctionComponent<DataGridProps> = props => {
const adaptableApiRef = React.useRef<AdaptableApi>();
const agGridModules: Module[] = [AllEnterpriseModule];;
const gridOptions = {
...props.adaptableOptions.gridOptions,
rowData: props.data,
};
return (
<div style={{display: 'flex', flexFlow: 'column', height: '100vh'}}>
<AdaptableReact
style={{flex: 'none'}}
gridOptions={gridOptions}
adaptableOptions={props.adaptableOptions}
onAdaptableReady={(adaptableReadyInfo: {
adaptableApi: AdaptableApi;
gridOptions: GridOptions;
}) => {
adaptableApiRef.current = adaptableReadyInfo.adaptableApi;
}}
/>
<div style={{flex: 1}}>
<AgGridReact gridOptions={gridOptions} modules={agGridModules} />
</div>
</div>
);
};- ConfigurationWizard.tsx provides the new first step of the Wizard
// API
const loadCars = () => {
/**
* A list of cars and their columns definitions
* {
* data: [],
* columnDefs: []
* }
*/
return fetch(API_BASE_CARS).then(res => res.json());
};
const loadAthletes = () => {
/**
* A list of athletes
* Athlete[]
*/
return fetch(API_BASE_ATHLETES).then(res => res.json());
};
const getColDefsFromData = (data: any[]) => {
const colDefs: ColDef[] = [];
const firstRow = data[0];
const getType = (value: any) => {
if (typeof value === 'string') {
return 'text';
}
if (typeof value === 'number') {
return 'number';
}
if (typeof value === 'boolean') {
return 'boolean';
}
return '';
};
for (const [key, value] of Object.entries(firstRow)) {
if (firstRow.hasOwnProperty(key)) {
colDefs.push({
headerName: key,
field: key,
type: getType(value),
});
}
}
return colDefs;
};
// UI
export interface ConfigurationWizardProps {
adaptableOptions: AdaptableOptions;
onAdaptableOptionsCreate: (config: {
adaptableOptions: AdaptableOptions;
data: any[];
}) => void;
}
interface DataSourceStepProps {
adaptableOptions: AdaptableOptions;
onChange: (adaptableOptions: AdaptableOptions) => void;
selectedDataSource: string;
setSelectedDataSource: (newDataSource: string) => void;
carsData: {data: any[]; columnDefs: ColDef[]} | null;
setCarsData: (data: {data: any[]; columnDefs: ColDef[]}) => void;
athletesData: any[] | null;
setAthletesData: (data: any[]) => void;
loading: boolean;
setLoading: (loading: boolean) => void;
}
const DataSourceStep: React.FunctionComponent<DataSourceStepProps> = props => {
/**
* Helper function to set the columnDefs in the adaptableOptions
* @param columnDefs
*/
const setColumnDefsInAdaptableOptions = (columnDefs: ColDef[]) => {
const adaptableId = 'nocode-demo-' + props.selectedDataSource;
const newAdaptableOptions: AdaptableOptions = {
...props.adaptableOptions,
adaptableId,
adaptableStateKey: adaptableId,
gridOptions: {
...props.adaptableOptions.gridOptions,
columnDefs: columnDefs,
},
};
props.onChange(newAdaptableOptions);
};
React.useEffect(() => {
/**
* Cars API has defined columns on the server
*/
if (props.selectedDataSource === 'cars') {
if (props.carsData) {
setColumnDefsInAdaptableOptions(props.carsData.columnDefs);
} else {
props.setLoading(true);
loadCars().then(data => {
setColumnDefsInAdaptableOptions(data.columnDefs);
props.setCarsData(data);
props.setLoading(false);
});
}
}
/**
* Athletes API has no defined columns on the server
* columns need to be derived from the data
*/
if (props.selectedDataSource === 'athletes') {
if (props.athletesData) {
const colDefs = getColDefsFromData(props.athletesData);
setColumnDefsInAdaptableOptions(colDefs);
} else {
props.setLoading(true);
loadAthletes().then(data => {
const colDefs = getColDefsFromData(data);
setColumnDefsInAdaptableOptions(colDefs);
props.setAthletesData(data);
props.setLoading(false);
});
}
}
}, [props.selectedDataSource]);
return (
<div style={{padding: 20}}>
<div style={{marginBottom: 20}}>
<b>Select one of the Custom Data Sources</b>
</div>
<select
value={props.selectedDataSource}
onChange={event =>
props.setSelectedDataSource(event.target.value.trim())
}>
<option value="athletes">Athletes</option>
<option value="cars">Cars</option>
</select>
<div>{props.loading && <b>Loading</b>}</div>
</div>
);
};
export const ConfigurationWizard: React.FunctionComponent<
ConfigurationWizardProps
> = props => {
const [loading, setLoading] = React.useState(false);
// State is one level above so it is not lost on step change
const [selectedDataSource, setSelectedDataSource] =
React.useState('athletes');
const [athletesData, setAthletesData] = React.useState<any[] | null>(null);
const [carsData, setCarsData] = React.useState<{
data: any[];
columnDefs: ColDef[];
} | null>(null);
return (
<AdaptableOptionsWizardView
skipToWizard
ddEnabled={false}
adaptableOptions={props.adaptableOptions}
onInit={newOptions =>
props.onAdaptableOptionsCreate({
adaptableOptions: newOptions,
data:
(selectedDataSource === 'athletes'
? athletesData
: carsData?.data) ?? [],
})
}
startSections={[
{
title: 'Data Source',
isValid: () => (loading ? 'Loading' : true),
render: (adaptableOptions, onChange, selectColumns) => {
const interceptOnChange = (adaptableOptions: AdaptableOptions) => {
// change the adaptableOptions
onChange(adaptableOptions);
// select new columns
const newColDefs = adaptableOptions.gridOptions
?.columnDefs as ColDef[];
if (newColDefs && newColDefs.length > 0) {
const selectedColumns = newColDefs.reduce((acc, col) => {
if (col.field) acc[col.field] = true;
return acc;
}, {} as Record<string, boolean>);
selectColumns(selectedColumns);
}
};
return (
<DataSourceStep
loading={loading}
setLoading={setLoading}
adaptableOptions={adaptableOptions}
onChange={interceptOnChange}
athletesData={athletesData}
carsData={carsData}
setAthletesData={setAthletesData}
setCarsData={setCarsData}
selectedDataSource={selectedDataSource}
setSelectedDataSource={setSelectedDataSource}
/>
);
},
},
]}
/>
);- Finally App.tsx references both components:
const App: React.FunctionComponent = () => {
const [adaptableOptions, setAdaptableOptions] =
React.useState<AdaptableOptions | null>(null);
const [data, setData] = React.useState<any[]>([]);
if (!adaptableOptions) {
return (
<ConfigurationWizard
adaptableOptions={{
...defaultAdaptableOptions,
gridOptions: {columnDefs: [{}]},
}}
onAdaptableOptionsCreate={config => {
setData(config.data);
setAdaptableOptions(config.adaptableOptions);
}}
/>
);
}
return <DataGrid adaptableOptions={adaptableOptions} data={data} />;
};
export default App; Try It Out
- Select a Data Source and see how it populates the next step of the Wizard (Columns)
import {WebFramework} from './rowData'; import { Adaptable, AdaptableOptions, AdaptablePercentageEditor, } from '@adaptabletools/adaptable'; export const adaptableOptions: AdaptableOptions<WebFramework> = { primaryKey: 'id', adaptableId: 'Adaptable Demo', initialState: { Layout: { CurrentLayout: 'Standard Layout', Layouts: [ { Name: 'Standard Layout', TableColumns: [ 'name', 'language', 'github_stars', 'license', 'created_at', 'has_wiki', 'updated_at', 'pushed_at', 'github_watchers', 'open_issues_count', 'closed_issues_count', 'open_pr_count', 'closed_pr_count', 'description', 'has_projects', 'has_pages', 'week_issue_change', ], }, ], }, }, };
Framework: