diff --git a/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.test.tsx b/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.test.tsx
index 1376c078e80ff..403c9f331255f 100644
--- a/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.test.tsx
+++ b/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.test.tsx
@@ -156,6 +156,50 @@ test('rendering and switching tabs for a non-standard role', async () => {
expect(screen.getByRole('button', { name: 'Update Role' })).toBeEnabled();
});
+test('switching tabs triggers validation', async () => {
+ // Triggering validation is necessary, because server-side yamlification
+ // sometimes will reject the data anyway.
+ render();
+ await user.clear(screen.getByLabelText('Role Name'));
+ expect(getStandardEditorTab()).toHaveAttribute('aria-selected', 'true');
+ await user.click(getYamlEditorTab());
+ expect(screen.getByLabelText('Role Name')).toHaveAccessibleDescription(
+ 'Role name is required'
+ );
+ // Expect to still be on the standard tab.
+ expect(getStandardEditorTab()).toHaveAttribute('aria-selected', 'true');
+});
+
+test('switching tabs ignores standard model validation for a non-standard role', async () => {
+ // The purpose of this test is to rule out a case where we start with a
+ // non-standard role that even after resetting would cause a validation
+ // error, then go to the standard editor only to be stuck there by the
+ // validation requirement, while the editor UI is itself blocked.
+ const originalRole = withDefaults({
+ // This will trigger a validation error. Note that empty metadata is a very
+ // blunt tool here, but that's certainly one case where we can guarantee
+ // it's gonna cause validation errors. The real-world scenarios would be
+ // much more subtle, but also more likely to get fixed in future, rendering
+ // this test case futile.
+ metadata: {},
+ spec: {},
+ unsupportedField: true, // This will cause disabling the standard editor.
+ } as any as Role);
+ render(
+
+ );
+ expect(getYamlEditorTab()).toHaveAttribute('aria-selected', 'true');
+ await user.click(getStandardEditorTab());
+ expect(
+ screen.getByText(/Some fields were not readable by the standard editor/)
+ ).toBeVisible();
+ await user.click(getYamlEditorTab());
+ // Proceed, even though our validation would consider the data invalid.
+ expect(getYamlEditorTab()).toHaveAttribute('aria-selected', 'true');
+});
+
test('no double conversions when clicking already active tabs', async () => {
render();
await user.click(getYamlEditorTab());
diff --git a/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.tsx b/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.tsx
index 6b084a65682c4..b109c82e8b87d 100644
--- a/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.tsx
+++ b/web/packages/teleport/src/Roles/RoleEditor/RoleEditor.tsx
@@ -130,7 +130,7 @@ export const RoleEditor = ({
// requires model to be valid. However, if it's OK, we reset the validator.
// We don't want it to be validating at this point, since the user didn't
// attempt to submit the form.
- if (!validator.validate()) return;
+ if (!standardModel.roleModel.requiresReset && !validator.validate()) return;
validator.reset();
switch (activeIndex) {