Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugins: Add grafana/user/profile/tab plugin extension point #77863

Merged

Conversation

joeyorlando
Copy link
Collaborator

@joeyorlando joeyorlando commented Nov 8, 2023

What is this feature?

This PR adds a new plugin component extension point, grafana/user/profile/tab, allowing plugins to render content on the /profile.

  • If no plugins have registered components against this extension point, the profile page is unchanged
  • If at least one plugin has registered a component, tabs are shown at the top of the page. Core is the default tab and represents the pre-existing settings. The remaining tab(s) are dependant based on what plugin(s) have registered against this extension point. For example, the following code within a plugin would result in this:
const extensionPointId = 'grafana/user/profile/settings';

const IRMSettingsTabTitle = 'IRM';
const OnCallSettingsTabTitle = 'OnCall';

plugin.configureExtensionComponent({
  title: IRMSettingsTabTitle,
  description: "", // not used with this extension point, but required by the TS type
  extensionPointId,
  component: MobileAppConnectionQRCode,
});

plugin.configureExtensionComponent({
  title: IRMSettingsTabTitle,
  description: "", // not used with this extension point, but required by the TS type
  extensionPointId,
  component: SomeMoreIRMSettings,
});

plugin.configureExtensionComponent({
  title: OnCallSettingsTabTitle,
  description: '', // not used with this extension point, but required by the TS type
  extensionPointId,
  component: GrafanaOnCallSettings,
});

Screenshot 2023-11-24 at 14 06 56

  • the /profile page will now look for a tab query parameter. If this is equal to any of the tab titles (case-insensitive), that tab will be rendered directly on page load.

Note: multiple plugins can render content to the same tab. Tabs + tab content are decided based on unique title attributes. Any duplicates are grouped together and rendered within the same tab (this will be useful for example in the "IRM" use-case where Grafana OnCall and Grafana Incident may each need to render some shared IRM-level settings; example mobile app QR code).

Why do we need this feature?

Grafana OnCall and Grafana Incident teams have a quarterly milestone to add Grafana Incident features to the IRM mobile app (previously OnCall mobile app). Part of this project involves moving the QR authentication code from within Grafana OnCall to somewhere "higher-up" within core Grafana. Since this is a shared setting between OnCall and Incident it made sense to move it to /profile.

Who is this feature for?

Plugin developers.

Which issue(s) does this PR fix?:

This unblocks grafana/oncall#3296

Please check that:

  • It works as expected from a user's perspective.
  • If this is a pre-GA feature, it is behind a feature toggle.
  • The docs are updated, and if this is a notable improvement, it's added to our What's New doc.

plugin extension point
@joeyorlando joeyorlando changed the title add grafana/user/profile/settings plugin extension point Add grafana/user/profile/settings plugin extension point Nov 24, 2023
@joeyorlando joeyorlando self-assigned this Nov 24, 2023
@joeyorlando joeyorlando marked this pull request as ready for review November 24, 2023 23:35
@joeyorlando joeyorlando requested review from a team as code owners November 24, 2023 23:35
@joeyorlando joeyorlando requested review from joshhunt, tskarhed and mckn and removed request for a team November 24, 2023 23:35
@joeyorlando joeyorlando changed the title Add grafana/user/profile/settings plugin extension point Plugins: Add grafana/user/profile/settings plugin extension point Nov 25, 2023
@joeyorlando joeyorlando changed the title Plugins: Add grafana/user/profile/settings plugin extension point Plugins: Add grafana/user/profile/tab plugin extension point Nov 25, 2023
@joeyorlando
Copy link
Collaborator Author

See grafana/plugin-tools#557 for docs update PR

Copy link
Contributor

@ashharrison90 ashharrison90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally looks good 👍

think we need to refactor slightly to make clear the separation between the tab query param (or id) and the tab title (or title). that's especially important for the core tab, because we'll want to translate that so we can't rely on the title.

shout if anything doesn't make sense 👍

});

it.each([tabOneName, tabOneName.toUpperCase(), tabOneName.toLowerCase()])(
'should set the active tab based on the "tab" query param and be case-insensitive',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why should it be case-insensitive? 🤔 afaik all our urls are supposed to be lower case

Copy link
Collaborator Author

@joeyorlando joeyorlando Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was overthinking edge-cases here. Your proposed approach of distinguishing between tab ids (always lowercased) & titles makes things simpler imo 👍

public/app/features/profile/UserProfileEditPage.tsx Outdated Show resolved Hide resolved
public/app/features/profile/UserProfileEditPage.tsx Outdated Show resolved Hide resolved
public/app/features/profile/UserProfileEditPage.tsx Outdated Show resolved Hide resolved
public/app/features/profile/UserProfileEditPage.tsx Outdated Show resolved Hide resolved
packages/grafana-e2e-selectors/src/selectors/components.ts Outdated Show resolved Hide resolved
public/app/features/profile/UserProfileEditPage.tsx Outdated Show resolved Hide resolved
public/app/features/profile/UserProfileEditPage.tsx Outdated Show resolved Hide resolved
</TabsBar>
<TabContent>
{activeTab === CORE_SETTINGS_TAB.toLowerCase() && <UserProfile />}
{toPairs(groupedExtensionComponents).map(([title, pluginExtensionComponents]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why use toPairs instead of Object.entries? 🤔

with the new structure, this will return [id, pluginExtensionComponents instead of [title, pluginExtensionComponents]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated this to use Object.entries, thanks for the suggestion! (this is how you know I'm (mostly) a backend engineer 🤓)

this:

Object.entries(groupedExtensionComponents).map(([title, pluginExtensionComponents])

will still return [title, pluginExtensionComponents] since I'm grouping the registered extension point components here by title. Nevertheless, I've added this small util function to convert a tab title to a tab id.

Copy link
Contributor

@ashharrison90 ashharrison90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accidentally approved when i meant to just leave a comment... so now i've gotta request changes 😂

Copy link
Contributor

@ashharrison90 ashharrison90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've pinged in our channel to see if we can get a second pair of 👀 on it just for a sanity check

one more thing @joshhunt pointed out, i think in https://raintank-corp.slack.com/archives/C064R17Q1A8/p1699964541932529 we discussed calling the default tab General instead of Core - can we make that change (and for the internal id as well). (sorry for not raising that sooner 🤦 )

@joeyorlando
Copy link
Collaborator Author

@ashharrison90 no worries, I've updated "Core" -> "General".

Question regarding the translation .json files (as this is my first grafana/grafana PR that touches these). Most of the diff'd .json files seem to have user-profile.tabs.general as blank. Also, I see in the docs you linked:

To update phrases in any translated language, edit the phrase in Crowdin. Do not edit the {locale}/grafana.json

I don't seem to have access to the project in crowdin (despite having access to grafana.crowdin.com). Anything more I need to do here or this is good as-is?

Copy link
Contributor

@ashharrison90 ashharrison90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm 🚀

let's see if we can get 1 more from @grafana/grafana-frontend-platform to approve as well 🙏

re: translations - nope, you're all set. all you need to make sure is that the english string (and pseudo string) is present, the empty strings in other languages is expected.

what happens is those keys are added to crowdin, then external translators will add the other language translations to crowdin. a github action then syncs the changes from crowdin back to the main repo.

@ashharrison90 ashharrison90 merged commit ea7a179 into main Dec 1, 2023
18 checks passed
@ashharrison90 ashharrison90 deleted the jorlando/add-user-profile-page-plugin-extension-point branch December 1, 2023 15:18
Comment on lines +71 to +75
const [queryParams, updateQueryParams] = useQueryParams();
const tabQueryParam = queryParams[TAB_QUERY_PARAM];
const [activeTab, setActiveTab] = useState<string>(
typeof tabQueryParam === 'string' ? tabQueryParam : GENERAL_SETTINGS_TAB
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use a query parameter here and not separate routes?

torkelo added a commit that referenced this pull request Dec 5, 2023
@aangelisc aangelisc modified the milestones: 10.3.x, 10.2.3 Dec 21, 2023
github-merge-queue bot pushed a commit to grafana/oncall that referenced this pull request Jan 10, 2024
# What this PR does

Dependent on grafana/grafana#77863

## Which issue(s) this PR fixes

## Checklist

- [ ] Unit, integration, and e2e (if applicable) tests updated
- [ ] Documentation added (or `pr:no public docs` PR label added if not
required)
- [ ] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)

---------

Co-authored-by: Rares Mardare <rares.mardare@grafana.com>
iskhakov pushed a commit to grafana/oncall that referenced this pull request Feb 20, 2024
# What this PR does

Dependent on grafana/grafana#77863

## Which issue(s) this PR fixes

## Checklist

- [ ] Unit, integration, and e2e (if applicable) tests updated
- [ ] Documentation added (or `pr:no public docs` PR label added if not
required)
- [ ] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)

---------

Co-authored-by: Rares Mardare <rares.mardare@grafana.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

6 participants