Skip to content

Commit

Permalink
PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
msmithNI committed Oct 31, 2024
1 parent e6d7531 commit bb9be66
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 208 deletions.
6 changes: 3 additions & 3 deletions packages/blazor-workspace/NimbleBlazor/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Test Project: `NimbleBlazor.Tests.Acceptance`

In order to fully test the Nimble Blazor components, consider writing new automated acceptance tests for new/modified components. Any component which requires custom JS code in `NimbleBlazor.lib.module.js` should generally have corresponding acceptance tests, because the bUnit tests in `NimbleBlazor.Tests` are unable to exercise/test that JavaScript code.

The `NimbleBlazor.Tests.Acceptance` project starts a local Blazor Web App which serves Razor pages that host the Nimble components. Then, xUnit-based acceptance tests start a Chromium instance using [Playwright](https://playwright.dev/), load those Razor pages, and interact with them. The majority of the tests use the `InteractiveServer` render mode, but the project also supports the Interactive Web Assembly render mode (and static server-side rendering mode) for tests.
The `NimbleBlazor.Tests.Acceptance` project starts a local Blazor Web App which serves Razor pages that host the Nimble components. Then, xUnit-based acceptance tests start a Chromium instance using [Playwright](https://playwright.dev/), load those Razor pages, and interact with them. The majority of the tests use the `InteractiveServer` render mode, but the project also supports the Interactive Web Assembly render mode (and static server-side rendering mode) for tests. Tests should disable prerendering (as shown in the steps below) to ensure the components are ready for interaction when the Playwright test starts.

To add a new acceptance test (with the Interactive Server render mode):
- Add a new Razor file that uses that component in the `Pages.InteractiveServer` subfolder, with the name `[ComponentName][FunctionalityUnderTest].razor`, e.g. `DialogOpenAndClose.razor`.
Expand All @@ -111,8 +111,8 @@ Visual Studio Code commands are included to build and run the example projects.
- `blazor-server-example:watch`: Run the `Demo.Server` project in watch mode (to automatically pick up code changes)
- `blazor-wasm-example:build`: Build the `Demo.Client` project
- `blazor-wasm-example:watch`: Run the `Demo.Client` project in watch mode (to automatically pick up code changes)
- `blazor-hybrid-example:build`: Build the `Demo.Hybrid` project
- `blazor-hybrid-example:watch`: Run the `Demo.Hybrid` project in watch mode (to automatically pick up code changes)
- `blazor-hybrid-example:build`: (Windows only) Build the `Demo.Hybrid` project
- `blazor-hybrid-example:watch`: (Windows only) Run the `Demo.Hybrid` project in watch mode (to automatically pick up code changes)

Also see the [trusting the ASP.NET Core development certificate docs](https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-8.0&tabs=visual-studio%2Clinux-sles#trust-the-aspnet-core-https-development-certificate-on-windows-and-macos).

Expand Down
2 changes: 1 addition & 1 deletion packages/blazor-workspace/NimbleBlazor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Nimble supports all of the [Blazor render modes](https://learn.microsoft.com/en-
- Interactive WebAssembly: Client-side rendering (CSR) using Blazor WebAssembly: `RenderMode.InteractiveWebAssembly`
- Interactive Auto: Interactive SSR initially, then CSR on subsequent visits after the Blazor bundle is downloaded: `RenderMode.InteractiveAuto`
- Static server-side rendering (static SSR): Default, when no render mode is specified
- ⚠️Warning: This render mode is not recommended for most use cases with Nimble. As the page is just rendered once server-side and then no state is maintained, you're unable to use event handlers or call methods on components. This also means that for components like the Nimble Table / Wafer Map, setting data can't be done vi the component methods (because they'll have no effect if called).
- ⚠️Warning: This render mode is not recommended for most use cases with Nimble. As the page is just rendered once server-side and then no state is maintained, you're unable to use event handlers or call methods on components. This also means that for components like the Nimble Table / Wafer Map, setting data can't be done via the component methods (because they'll have no effect if called).

#### Prerendering

Expand Down
322 changes: 158 additions & 164 deletions packages/blazor-workspace/NimbleBlazor/wwwroot/NimbleBlazor.lib.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,200 +9,194 @@
* https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/startup?view=aspnetcore-8.0
*/

const initializer = (function () {
let hasRegisteredEvents = false;
let hasRegisteredEvents = false;
let isReady = false;

function registerEvents(Blazor) {
if (hasRegisteredEvents) {
return;
}
function registerEvents(Blazor) {
if (hasRegisteredEvents) {
return;
}

if (!Blazor) {
throw new Error('Blazor not ready to initialize Nimble with!');
}
if (!Blazor) {
throw new Error('Blazor not ready to initialize Nimble with!');
}

hasRegisteredEvents = true;
hasRegisteredEvents = true;

// Used by NimbleCheckbox.razor, NimbleSwitch.razor, NimbleToggleButton.razor
// Necessary because the control's value property is always just the value 'on', so we need to look
// at the checked property to correctly get the value.
Blazor.registerCustomEventType('nimblecheckedchange', {
browserEventName: 'change',
createEventArgs: event => {
return {
checked: event.target.currentChecked
};
}
});
// Used by NimbleTabs.razor
// Necessary because the tab control uses a 'change' event but not a value/currentValue property,
// and we do want to be notified of activeid changes (via the change event) for 2-way binding support.
// 'localName' check is required to guard against children's change event trickling into the NimbleTabs.
Blazor.registerCustomEventType('nimbletabsactiveidchange', {
browserEventName: 'change',
createEventArgs: event => {
if (event.target.localName === 'nimble-tabs') {
return {
activeId: event.target.activeid
};
}
return null;
}
});
// Used by NimbleMenuButton.razor
Blazor.registerCustomEventType('nimblemenubuttontoggle', {
browserEventName: 'toggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleMenuButton.razor
Blazor.registerCustomEventType('nimblemenubuttonbeforetoggle', {
browserEventName: 'beforetoggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleBanner.razor
Blazor.registerCustomEventType('nimblebannertoggle', {
browserEventName: 'toggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbleactionmenubeforetoggle', {
browserEventName: 'action-menu-beforetoggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState,
recordIds: event.detail.recordIds,
columnId: event.detail.columnId
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbleactionmenutoggle', {
browserEventName: 'action-menu-toggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState,
recordIds: event.detail.recordIds,
columnId: event.detail.columnId
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbletablerowselectionchange', {
browserEventName: 'selection-change',
createEventArgs: event => {
return {
selectedRecordIds: event.detail.selectedRecordIds
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbletablecolumnconfigurationchange', {
browserEventName: 'column-configuration-change',
createEventArgs: event => {
return {
columns: event.detail.columns
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbletablerowexpandtoggle', {
browserEventName: 'row-expand-toggle',
createEventArgs: event => {
return {
recordId: event.detail.recordId,
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleTableColumnMenuButton.razor
Blazor.registerCustomEventType('nimbletablecolumnmenubuttonbeforetoggle', {
browserEventName: 'menu-button-column-beforetoggle',
createEventArgs: event => {
return {
recordId: event.detail.recordId,
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleTableColumnMenuButton.razor
Blazor.registerCustomEventType('nimbletablecolumnmenubuttontoggle', {
browserEventName: 'menu-button-column-toggle',
createEventArgs: event => {
return {
recordId: event.detail.recordId,
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleWaferMap.razor
Blazor.registerCustomEventType('nimblewafermapdiehoverchange', {
browserEventName: 'die-hover',
createEventArgs: event => {
// Used by NimbleCheckbox.razor, NimbleSwitch.razor, NimbleToggleButton.razor
// Necessary because the control's value property is always just the value 'on', so we need to look
// at the checked property to correctly get the value.
Blazor.registerCustomEventType('nimblecheckedchange', {
browserEventName: 'change',
createEventArgs: event => {
return {
checked: event.target.currentChecked
};
}
});
// Used by NimbleTabs.razor
// Necessary because the tab control uses a 'change' event but not a value/currentValue property,
// and we do want to be notified of activeid changes (via the change event) for 2-way binding support.
// 'localName' check is required to guard against children's change event trickling into the NimbleTabs.
Blazor.registerCustomEventType('nimbletabsactiveidchange', {
browserEventName: 'change',
createEventArgs: event => {
if (event.target.localName === 'nimble-tabs') {
return {
currentDie: event.detail.currentDie
activeId: event.target.activeid
};
}
});
}

function handleRuntimeStarted() {
window.NimbleBlazor.isInitialized = true;
}
return null;
}
});
// Used by NimbleMenuButton.razor
Blazor.registerCustomEventType('nimblemenubuttontoggle', {
browserEventName: 'toggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleMenuButton.razor
Blazor.registerCustomEventType('nimblemenubuttonbeforetoggle', {
browserEventName: 'beforetoggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleBanner.razor
Blazor.registerCustomEventType('nimblebannertoggle', {
browserEventName: 'toggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbleactionmenubeforetoggle', {
browserEventName: 'action-menu-beforetoggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState,
recordIds: event.detail.recordIds,
columnId: event.detail.columnId
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbleactionmenutoggle', {
browserEventName: 'action-menu-toggle',
createEventArgs: event => {
return {
newState: event.detail.newState,
oldState: event.detail.oldState,
recordIds: event.detail.recordIds,
columnId: event.detail.columnId
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbletablerowselectionchange', {
browserEventName: 'selection-change',
createEventArgs: event => {
return {
selectedRecordIds: event.detail.selectedRecordIds
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbletablecolumnconfigurationchange', {
browserEventName: 'column-configuration-change',
createEventArgs: event => {
return {
columns: event.detail.columns
};
}
});
// Used by NimbleTable.razor
Blazor.registerCustomEventType('nimbletablerowexpandtoggle', {
browserEventName: 'row-expand-toggle',
createEventArgs: event => {
return {
recordId: event.detail.recordId,
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleTableColumnMenuButton.razor
Blazor.registerCustomEventType('nimbletablecolumnmenubuttonbeforetoggle', {
browserEventName: 'menu-button-column-beforetoggle',
createEventArgs: event => {
return {
recordId: event.detail.recordId,
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleTableColumnMenuButton.razor
Blazor.registerCustomEventType('nimbletablecolumnmenubuttontoggle', {
browserEventName: 'menu-button-column-toggle',
createEventArgs: event => {
return {
recordId: event.detail.recordId,
newState: event.detail.newState,
oldState: event.detail.oldState
};
}
});
// Used by NimbleWaferMap.razor
Blazor.registerCustomEventType('nimblewafermapdiehoverchange', {
browserEventName: 'die-hover',
createEventArgs: event => {
return {
currentDie: event.detail.currentDie
};
}
});
}

return {
registerEvents,
handleRuntimeStarted
};
}());
function handleRuntimeStarted() {
isReady = true;
}

// Blazor Web Apps
export function afterWebStarted(Blazor) {
initializer.registerEvents(Blazor);
registerEvents(Blazor);
}

// Blazor Web Apps using InteractiveServer render mode
export function afterServerStarted(_Blazor) {
initializer.handleRuntimeStarted();
handleRuntimeStarted();
}

// Blazor Web Apps using InteractiveWebAssembly render mode; WASM Standalone apps
export function afterWebAssemblyStarted(_Blazor) {
initializer.registerEvents(Blazor);
initializer.handleRuntimeStarted();
registerEvents(Blazor);
handleRuntimeStarted();
}

// Blazor Hybrid apps
export function afterStarted(Blazor) {
initializer.registerEvents(Blazor);
initializer.handleRuntimeStarted();
registerEvents(Blazor);
handleRuntimeStarted();
}

if (window.NimbleBlazor) {
console.warn('Attempting to initialize NimbleBlazor multiple times!'); // eslint-disable-line
}

window.NimbleBlazor = window.NimbleBlazor ?? {
isInitialized: false,
isReady: () => isReady,
Dialog: {
show: async function (dialogReference) {
const reason = await dialogReference.show();
Expand Down
Loading

0 comments on commit bb9be66

Please sign in to comment.