Create FF for 'Alignments' Menu Option in IOM

closes OUT-5743
flag=improved_outcomes_management

The 'Alignments' kebab menu option in IOM should be
hidden behind a 'Site-Admin' level feature flag

Test plan:
- Tests are sufficient and pass in Jenkins
- Ensure that the "Menu Option for Outcome Details Page" FF is
  only visible for 'Site-Admin' level users
- Toggle the FF and ensure that the 'Alignments' option only shows
  when the FF is enabled

Change-Id: I89b6209056b0ea57c0140ae40254bbbc3241e3e4
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/321694
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Chrystal Langston <chrystal.langston@instructure.com>
QA-Review: Martin Yosifov <martin.yosifov@instructure.com>
Product-Review: Kyle Rosenbaum <krosenbaum@instructure.com>
This commit is contained in:
Jason Anderson 2023-06-29 12:05:01 -06:00 committed by Kyle Rosenbaum
parent 2fbf3f644d
commit 498f788e81
7 changed files with 89 additions and 14 deletions

View File

@ -56,7 +56,8 @@ class OutcomesController < ApplicationController
@context.grants_right?(@current_user, session, :manage_proficiency_calculations)
},
OUTCOMES_FRIENDLY_DESCRIPTION: Account.site_admin.feature_enabled?(:outcomes_friendly_description),
OUTCOME_AVERAGE_CALCULATION: @context.root_account.feature_enabled?(:outcome_average_calculation)
OUTCOME_AVERAGE_CALCULATION: @context.root_account.feature_enabled?(:outcome_average_calculation),
MENU_OPTION_FOR_OUTCOME_DETAILS_PAGE: Account.site_admin.feature_enabled?(:menu_option_for_outcome_details_page)
)
set_tutorial_js_env

View File

@ -62,6 +62,12 @@ improved_lmgb:
state: disabled
beta:
state: disabled
menu_option_for_outcome_details_page:
type: setting
state: hidden
applies_to: SiteAdmin
display_name: Menu Option for Outcome Details Page
description: Adds menu option for accessing alignments at the outcome level with Improved Outcomes Management
outcomes_friendly_description:
state: hidden
applies_to: SiteAdmin

View File

@ -179,6 +179,22 @@ describe OutcomesController do
end
end
context "menu_option_for_outcome_details_page" do
it "returns true if menu_option_for_outcome_details_page feature flage is enabled" do
Account.site_admin.enable_feature!(:menu_option_for_outcome_details_page)
user_session(@admin)
get "index", params: { account_id: @account.id }
expect(assigns[:js_env][:MENU_OPTION_FOR_OUTCOME_DETAILS_PAGE]).to be true
end
it "returns false if menu_option_for_outcome_details_page feature flage is disabled" do
Account.site_admin.disable_feature!(:menu_option_for_outcome_details_page)
user_session(@admin)
get "index", params: { account_id: @account.id }
expect(assigns[:js_env][:MENU_OPTION_FOR_OUTCOME_DETAILS_PAGE]).to be false
end
end
describe "GET 'show'" do
it "requires authorization" do
course_outcome

View File

@ -33,6 +33,7 @@ import {
} from '@instructure/ui-icons'
import {useScope as useI18nScope} from '@canvas/i18n'
import {stripHtmlTags} from '@canvas/outcomes/stripHtmlTags'
import useCanvasContext from '@canvas/outcomes/react/hooks/useCanvasContext'
const I18n = useI18nScope('OutcomeManagement')
@ -44,6 +45,7 @@ const OutcomeKebabMenu = ({
isGroup,
groupDescription,
}) => {
const {menuOptionForOutcomeDetailsPageFF} = useCanvasContext()
const hasDescription =
typeof groupDescription === 'string' &&
stripHtmlTags(groupDescription).replace(/[\n\r\t\s(&nbsp;)]+/g, '')
@ -65,7 +67,7 @@ const OutcomeKebabMenu = ({
{I18n.t('Edit')}
</View>
</Menu.Item>
{!isGroup && (
{menuOptionForOutcomeDetailsPageFF && !isGroup && (
<Menu.Item value="alignments">
<IconOutcomesLine size="x-small" />
<View padding="0 x-large 0 small" data-testid="outcome-kebab-menu-alignments">

View File

@ -20,6 +20,7 @@ import React from 'react'
import {render, fireEvent} from '@testing-library/react'
import {merge} from 'lodash'
import OutcomeKebabMenu from '../OutcomeKebabMenu'
import OutcomesContext from '@canvas/outcomes/react/contexts/OutcomesContext'
describe('OutcomeKebabMenu', () => {
let onMenuHandlerMock
@ -36,6 +37,25 @@ describe('OutcomeKebabMenu', () => {
props
)
const renderWithProvider = (
children,
{contextType = 'Account', contextId = '1', menuOptionForOutcomeDetailsPageFF = true} = {}
) => {
return render(
<OutcomesContext.Provider
value={{
env: {
contextType,
contextId,
menuOptionForOutcomeDetailsPageFF,
},
}}
>
{children}
</OutcomesContext.Provider>
)
}
beforeEach(() => {
onMenuHandlerMock = jest.fn()
})
@ -98,18 +118,42 @@ describe('OutcomeKebabMenu', () => {
expect(getByText('Import Outcomes')).toBeInTheDocument()
})
it('renders Alignments in Kebab menu if a isGroup is not provided and menu button clicked', () => {
const {getByText} = render(<OutcomeKebabMenu {...defaultProps({isGroup: false})} />)
const menuButton = getByText(groupMenuTitle)
fireEvent.click(menuButton)
expect(getByText('Alignments')).toBeInTheDocument()
})
describe('Menu Option for Outcome Details Page FF', () => {
it('does not render Alignments menu option if Menu Option for Outcome Details Page FF is disabled', () => {
const {queryByText} = renderWithProvider(
<OutcomeKebabMenu {...defaultProps({isGroup: false})} />,
{
menuOptionForOutcomeDetailsPageFF: false,
}
)
const menuButton = queryByText(groupMenuTitle)
fireEvent.click(menuButton)
expect(queryByText('Alignments')).not.toBeInTheDocument()
})
it('does not render Alignments in Kebab menu if a isGroup is provided and menu button clicked', () => {
const {queryByText} = render(<OutcomeKebabMenu {...defaultProps({isGroup: true})} />)
const menuButton = queryByText(groupMenuTitle)
fireEvent.click(menuButton)
expect(queryByText('Alignments')).not.toBeInTheDocument()
it('renders Alignments in Kebab menu if a isGroup is not provided and menu button clicked and FF is enabled', () => {
const {getByText} = renderWithProvider(
<OutcomeKebabMenu {...defaultProps({isGroup: false})} />,
{
menuOptionForOutcomeDetailsPageFF: true,
}
)
const menuButton = getByText(groupMenuTitle)
fireEvent.click(menuButton)
expect(getByText('Alignments')).toBeInTheDocument()
})
it('does not render Alignments in Kebab menu if a isGroup is provided and menu button clicked and FF is disabled', () => {
const {queryByText} = renderWithProvider(
<OutcomeKebabMenu {...defaultProps({isGroup: true})} />,
{
menuOptionForOutcomeDetailsPageFF: true,
}
)
const menuButton = queryByText(groupMenuTitle)
fireEvent.click(menuButton)
expect(queryByText('Alignments')).not.toBeInTheDocument()
})
})
describe('with Kebab menu open', () => {
@ -144,7 +188,9 @@ describe('OutcomeKebabMenu', () => {
})
it('handles click on Alignments item', () => {
const {getByText} = render(<OutcomeKebabMenu {...defaultProps()} />)
const {getByText} = renderWithProvider(<OutcomeKebabMenu {...defaultProps()} />, {
menuOptionForOutcomeDetailsPageFF: true,
})
const menuButton = getByText(groupMenuTitle)
fireEvent.click(menuButton)
const menuItem = getByText('Alignments')

View File

@ -38,6 +38,7 @@ export const getContext = isMobileView => {
const rootIds = [globalRootId, treeBrowserAccountGroupId, treeBrowserRootGroupId]
const accountLevelMasteryScalesFF = ENV.ACCOUNT_LEVEL_MASTERY_SCALES
const outcomeAllowAverageCalculationFF = ENV.OUTCOME_AVERAGE_CALCULATION
const menuOptionForOutcomeDetailsPageFF = ENV.MENU_OPTION_FOR_OUTCOME_DETAILS_PAGE
return {
env: {
@ -56,6 +57,7 @@ export const getContext = isMobileView => {
rootIds,
accountLevelMasteryScalesFF,
outcomeAllowAverageCalculationFF,
menuOptionForOutcomeDetailsPageFF,
},
}
}

View File

@ -37,6 +37,7 @@ const useCanvasContext = () => {
const rootIds = context?.env?.rootIds
const accountLevelMasteryScalesFF = context?.env?.accountLevelMasteryScalesFF
const outcomeAllowAverageCalculationFF = context?.env?.outcomeAllowAverageCalculationFF
const menuOptionForOutcomeDetailsPageFF = context?.env?.menuOptionForOutcomeDetailsPageFF
return {
contextType,
@ -55,6 +56,7 @@ const useCanvasContext = () => {
rootIds,
accountLevelMasteryScalesFF,
outcomeAllowAverageCalculationFF,
menuOptionForOutcomeDetailsPageFF,
}
}