Skip to content

Commit

Permalink
Merge pull request #45205 from code-dot-org/staging
Browse files Browse the repository at this point in the history
DTT (Staging > Test) [robo-dtt]
  • Loading branch information
deploy-code-org committed Mar 8, 2022
2 parents 4989741 + 08bb53d commit 3deb66f
Show file tree
Hide file tree
Showing 206 changed files with 10,626 additions and 712 deletions.
28 changes: 6 additions & 22 deletions apps/src/lib/levelbuilder/code-docs-editor/ExampleEditor.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import PropTypes from 'prop-types';
import React, {useState} from 'react';
import Button from '@cdo/apps/templates/Button';
import React from 'react';
import color from '@cdo/apps/util/color';
import HelpTip from '@cdo/apps/lib/ui/HelpTip';
import TextareaWithMarkdownPreview from '@cdo/apps/lib/levelbuilder/TextareaWithMarkdownPreview';
import UploadImageDialog from '@cdo/apps/lib/levelbuilder/lesson-editor/UploadImageDialog';
import ImageInput from './ImageInput';

const APP_DISPLAY_OPTIONS = {
embedAppWithCode: 'Embed app with code directly',
Expand All @@ -14,8 +13,6 @@ const APP_DISPLAY_OPTIONS = {
const DEFAULT_EMBED_HEIGHT = 310;

export default function ExampleEditor({example, updateExample}) {
const [uploadImageDialogOpen, setUploadImageDialogOpen] = useState(false);

return (
<div>
<label>
Expand Down Expand Up @@ -47,17 +44,10 @@ export default function ExampleEditor({example, updateExample}) {
style={styles.textInput}
/>
</label>
<label>
Image
<Button
onClick={() => setUploadImageDialogOpen(true)}
text="Choose Image"
color="gray"
icon="plus-circle"
/>
{example.imageUrl && <span>{example.imageUrl}</span>}
</label>

<ImageInput
imageUrl={example.image}
updateImageUrl={img => updateExample('image', img)}
/>
<label>
Example App Display Type
<select
Expand Down Expand Up @@ -89,12 +79,6 @@ export default function ExampleEditor({example, updateExample}) {
style={styles.textInput}
/>{' '}
</label>
<UploadImageDialog
isOpen={uploadImageDialogOpen}
handleClose={() => setUploadImageDialogOpen(false)}
uploadImage={imgUrl => updateExample('image', imgUrl)}
allowExpandable={false}
/>
</div>
);
}
Expand Down
54 changes: 54 additions & 0 deletions apps/src/lib/levelbuilder/code-docs-editor/ImageInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import Button from '@cdo/apps/templates/Button';
import StylizedBaseDialog from '@cdo/apps/componentLibrary/StylizedBaseDialog';
import UploadImageDialog from '@cdo/apps/lib/levelbuilder/lesson-editor/UploadImageDialog';

export default function ImageInput({updateImageUrl, imageUrl}) {
const [removeImageDialogOpen, setRemoveImageDialogOpen] = useState(false);
const [uploadImageDialogOpen, setUploadImageDialogOpen] = useState(false);
return (
<div>
<label>
Image
<Button
onClick={() => setUploadImageDialogOpen(true)}
text={!!imageUrl ? 'Replace Image' : 'Choose Image'}
color="gray"
icon="plus-circle"
/>
{imageUrl && <span>{imageUrl}</span>}
{imageUrl && (
<Button
text="Remove Image"
color="red"
icon="trash"
onClick={() => setRemoveImageDialogOpen(true)}
/>
)}
</label>
{removeImageDialogOpen && (
<StylizedBaseDialog
body="Are you sure you want to remove this image?"
handleConfirmation={() => {
updateImageUrl(null);
setRemoveImageDialogOpen(false);
}}
handleClose={() => setRemoveImageDialogOpen(false)}
isOpen
/>
)}
<UploadImageDialog
isOpen={uploadImageDialogOpen}
handleClose={() => setUploadImageDialogOpen(false)}
uploadImage={imgUrl => updateImageUrl(imgUrl)}
allowExpandable={false}
/>
</div>
);
}

ImageInput.propTypes = {
updateImageUrl: PropTypes.func.isRequired,
imageUrl: PropTypes.string
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import React, {useState} from 'react';
import OrderableList from './OrderableList';
import ExampleEditor from './ExampleEditor';
import ParameterEditor from './ParameterEditor';
import ImageInput from './ImageInput';
import TextareaWithMarkdownPreview from '@cdo/apps/lib/levelbuilder/TextareaWithMarkdownPreview';
import CollapsibleEditorSection from '@cdo/apps/lib/levelbuilder/CollapsibleEditorSection';
import HelpTip from '@cdo/apps/lib/ui/HelpTip';
import SaveBar from '@cdo/apps/lib/levelbuilder/SaveBar';
import Button from '@cdo/apps/templates/Button';
import UploadImageDialog from '@cdo/apps/lib/levelbuilder/lesson-editor/UploadImageDialog';
import {createUuid, navigateToHref} from '@cdo/apps/utils';
import $ from 'jquery';
import color from '@cdo/apps/util/color';
Expand Down Expand Up @@ -66,7 +65,6 @@ export default function ProgrammingExpressionEditor({
const [isSaving, setIsSaving] = useState(false);
const [lastUpdated, setLastUpdated] = useState(null);
const [error, setError] = useState(null);
const [uploadImageDialogOpen, setUploadImageDialogOpen] = useState(false);

const save = (e, shouldCloseAfterSave) => {
if (isSaving) {
Expand Down Expand Up @@ -152,18 +150,12 @@ export default function ProgrammingExpressionEditor({
))}
</select>
</label>
<label>
Image
<Button
onClick={() => setUploadImageDialogOpen(true)}
text="Choose Image"
color="gray"
icon="plus-circle"
/>
{programmingExpression.imageUrl && (
<span>{programmingExpression.imageUrl}</span>
)}
</label>
<ImageInput
updateImageUrl={imgUrl =>
updateProgrammingExpression('imageUrl', imgUrl)
}
imageUrl={programmingExpression.imageUrl}
/>
<label>
Short Description
<textarea
Expand Down Expand Up @@ -277,12 +269,6 @@ export default function ProgrammingExpressionEditor({
error={error}
handleView={() => navigateToHref(showPath)}
/>
<UploadImageDialog
isOpen={uploadImageDialogOpen}
handleClose={() => setUploadImageDialogOpen(false)}
uploadImage={imgUrl => updateProgrammingExpression('imageUrl', imgUrl)}
allowExpandable={false}
/>
</div>
);
}
Expand Down
6 changes: 3 additions & 3 deletions apps/src/templates/codeDocs/Example.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function Example({example, programmingEnvironmentName}) {
...embeddedIdeStyles[programmingEnvironmentName]
}}
/>
{example.imageUrl && <img src={example.imageUrl} />}
{example.image && <img src={example.image} />}
</div>
);
} else {
Expand All @@ -46,15 +46,15 @@ export default function Example({example, programmingEnvironmentName}) {
}}
/>
</div>
{example.imageUrl && <img src={example.imageUrl} />}
{example.image && <img src={example.image} />}
</div>
);
}
} else {
return (
<div>
{content}
{example.imageUrl && <img src={example.imageUrl} />}
{example.image && <img src={example.image} />}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,9 @@ describe('ExampleEditor', () => {
);
});

it('displays image upload dialog when button is pressed', () => {
it('displays ImageInput for image', () => {
const wrapper = shallow(<ExampleEditor {...defaultProps} />);
const uploadButton = wrapper.find('Button').first();
expect(uploadButton).to.not.be.null;
uploadButton.simulate('click');
expect(wrapper.find('UploadImageDialog').length).to.equal(1);
expect(wrapper.find('ImageInput').length).to.equal(1);
});

it('displays a select for display type', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import {shallow} from 'enzyme';
import {expect} from '../../../../util/reconfiguredChai';
import ImageInput from '@cdo/apps/lib/levelbuilder/code-docs-editor/ImageInput';

describe('ImageInput', () => {
it('displays image upload dialog when upload image button is pressed', () => {
const wrapper = shallow(<ImageInput updateImageUrl={() => {}} />);
const uploadButton = wrapper.find('Button').first();
expect(uploadButton).to.not.be.null;
uploadButton.simulate('click');
expect(wrapper.find('UploadImageDialog').length).to.equal(1);
});

it('shows imageUrl if one is provided', () => {
const wrapper = shallow(
<ImageInput
updateImageUrl={() => {}}
imageUrl="code.org/images/spritelab.png"
/>
);
expect(wrapper.text().includes('code.org/images/spritelab.png')).to.be.true;
});

it('shows confirmation dialog if remove image button is pressed', () => {
const wrapper = shallow(
<ImageInput
updateImageUrl={() => {}}
imageUrl="code.org/images/spritelab.png"
/>
);
expect(wrapper.find('Button').length).to.equal(2);
const removeButton = wrapper.find('Button').last();
removeButton.simulate('click');
expect(wrapper.find('StylizedBaseDialog').length).to.equal(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,7 @@ describe('ProgrammingExpressionEditor', () => {
).to.eql(['', 'video1', 'video2']);

// Image upload
expect(
wrapper
.find('Button')
.at(0)
.props().text
).to.equal('Choose Image');
expect(wrapper.find('ImageInput').length).to.equal(1);

// short description
expect(
Expand Down Expand Up @@ -173,14 +168,6 @@ describe('ProgrammingExpressionEditor', () => {
expect(blockNameInput.props().value).to.equal('gamelab_location_picker');
});

it('shows upload image dialog when choose image button is pressed', () => {
const wrapper = shallow(<ProgrammingExpressionEditor {...defaultProps} />);
const uploadButton = wrapper.find('Button').first();
expect(uploadButton).to.not.be.null;
uploadButton.simulate('click');
expect(wrapper.find('UploadImageDialog').length).to.equal(1);
});

it('attempts to save when save is pressed', () => {
const store = getStore();
const wrapper = mount(
Expand Down
2 changes: 1 addition & 1 deletion apps/test/unit/templates/codeDocs/ExampleTest.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('Example', () => {
description: 'An example',
code: '```This is some example code```',
app_display_type: 'codeFromCodeField',
imageUrl: '/image.png'
image: '/image.png'
};

const wrapper = shallow(
Expand Down
19 changes: 16 additions & 3 deletions dashboard/app/controllers/programming_environments_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class ProgrammingEnvironmentsController < ApplicationController
load_and_authorize_resource

before_action :require_levelbuilder_mode_or_test_env, except: [:index, :show]
before_action :set_programming_environment, except: [:index, :new, :create]
authorize_resource

def index
@programming_environments = ProgrammingEnvironment.all.order(:name).map(&:summarize_for_index)
Expand Down Expand Up @@ -56,11 +56,20 @@ def update
end

def show
@programming_environment = ProgrammingEnvironment.find_by_name(params[:name])
return render :not_found unless @programming_environment
@programming_environment_categories = @programming_environment.categories.select {|c| c.programming_expressions.count > 0}.map(&:summarize_for_environment_show)
end

def destroy
return render :not_found unless @programming_environment
begin
@programming_environment.destroy!
render(status: 200, plain: "Destroyed #{@programming_environment.name}")
rescue => e
render(status: :not_acceptable, plain: e.message)
end
end

private

def programming_environment_params
Expand All @@ -75,4 +84,8 @@ def programming_environment_params
)
transformed_params
end

def set_programming_environment
@programming_environment = ProgrammingEnvironment.find_by_name(params[:name])
end
end
22 changes: 22 additions & 0 deletions dashboard/app/controllers/programming_expressions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@ def show_by_keys
render :not_found
end

def destroy
return render :not_found unless @programming_expression
begin
@programming_expression.destroy
render(status: 200, plain: "Destroyed #{@programming_expression.name}")
rescue
render(status: :not_acceptable, plain: @programming_expression.errors.full_messages.join('. '))
end
end

# POST /programming_expressions/:id/clone
def clone
return render :not_found unless @programming_expression
return render :not_acceptable unless params[:destinationProgrammingEnvironmentName]
begin
new_exp = @programming_expression.clone_to_programming_environment(params[:destinationProgrammingEnvironmentName], params[:destinationCategoryKey])
render(status: 200, json: {editUrl: edit_programming_expression_path(new_exp)})
rescue => err
render(json: {error: err.message}.to_json, status: :not_acceptable)
end
end

private

def programming_expression_params
Expand Down
11 changes: 10 additions & 1 deletion dashboard/app/models/programming_environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class ProgrammingEnvironment < ApplicationRecord
has_many :programming_environment_categories, -> {order(:position)}, dependent: :destroy
has_many :programming_expressions, dependent: :destroy

after_destroy :remove_serialization

# @attr [String] editor_type - Type of editor one of the following: 'text-based', 'droplet', 'blockly'
serialized_attrs %w(
editor_type
Expand Down Expand Up @@ -62,6 +64,10 @@ def self.seed_record(file_path)
environment.name
end

def file_path
Rails.root.join("config/programming_environments/#{name.parameterize}.json")
end

def serialize
env_hash = {name: name}.merge(properties.sort.to_h)
env_hash.merge(categories: programming_environment_categories.map(&:serialize))
Expand All @@ -70,10 +76,13 @@ def serialize
def write_serialization
return unless Rails.application.config.levelbuilder_mode

file_path = Rails.root.join("config/programming_environments/#{name.parameterize}.json")
File.write(file_path, JSON.pretty_generate(serialize))
end

def remove_serialization
File.delete(file_path) if File.exist?(file_path)
end

def summarize_for_lesson_edit
{id: id, name: name}
end
Expand Down

0 comments on commit 3deb66f

Please sign in to comment.